Rcov Code Coverage Results

C0 code coverage information.

Total Coverage: 45.2%

File Path Percent run
lib/chef/mixin/command/unix.rb 14
lib/chef/platform.rb 15
lib/chef/provider/package/macports.rb 19
lib/chef/shef/ext.rb 20
lib/chef/provider/cron.rb 21
lib/chef/provider/package/yum.rb 22
lib/chef/certificate.rb 23
lib/chef/provider/cron/solaris.rb 23
lib/chef/knife/cookbook_create.rb 24
lib/chef/couchdb.rb 26
lib/chef/cookbook_site_streaming_uploader.rb 27
lib/chef/provider/mount/mount.rb 27
lib/chef/provider/service/freebsd.rb 27
lib/chef/provider/ifconfig.rb 27
lib/chef/shell_out/unix.rb 27
lib/chef/provider/deploy.rb 28
lib/chef/provider/package/zypper.rb 28
lib/chef/resource/cron.rb 29
lib/chef/resource.rb 29
lib/chef/cookbook/cookbook_version_loader.rb 29
lib/chef/provider/service/upstart.rb 29
lib/chef/node/attribute.rb 29
lib/chef/provider/service/simple.rb 30
lib/chef/provider/link.rb 30
/home/pratima/.chef/plugins/knife/envstatus.rb 30
lib/chef/cookbook_uploader.rb 31
lib/chef/provider/service/debian.rb 32
lib/chef/provider/package/portage.rb 32
lib/chef/provider/package/solaris.rb 32
lib/chef/knife/help.rb 32
lib/chef/provider/erl_call.rb 32
lib/chef/provider/package/apt.rb 32
lib/chef/provider/package.rb 32
lib/chef/monkey_patches/object.rb 33
lib/chef/knife/cookbook_site_share.rb 33
lib/chef/rest.rb 34
lib/chef/provider/user/useradd.rb 34
lib/chef/provider/git.rb 35
lib/chef/provider/package/dpkg.rb 35
lib/chef/provider/user/dscl.rb 35
lib/chef/resource/mount.rb 35
lib/chef/resource/ifconfig.rb 35
lib/chef/resource/execute.rb 35
lib/chef/provider/user.rb 35
lib/chef/provider/service/windows.rb 35
lib/chef/knife/cookbook_delete.rb 36
lib/chef/provider/service/arch.rb 36
lib/chef/knife/configure.rb 36
lib/chef/provider/package/rpm.rb 36
lib/chef/cookbook_version.rb 36
lib/chef/resource/route.rb 36
lib/chef/cookbook_version_selector.rb 36
lib/chef/environment.rb 36
lib/chef/resource/user.rb 36
lib/chef/resource/scm.rb 36
lib/chef/resource_collection.rb 37
lib/chef/provider/user/pw.rb 37
lib/chef/provider/package/easy_install.rb 37
lib/chef/provider/http_request.rb 38
lib/chef/resource/remote_directory.rb 38
lib/chef/knife/status.rb 38
lib/chef/knife/cookbook_upload.rb 38
lib/chef/provider/route.rb 38
lib/chef/provider/package/pacman.rb 38
lib/chef/knife/core/cookbook_scm_repo.rb 38
lib/chef/mixin/params_validate.rb 39
lib/chef/resource/file.rb 39
lib/chef/knife/core/generic_presenter.rb 39
lib/chef/rest/rest_request.rb 39
lib/chef/provider/service/systemd.rb 39
lib/chef/version_constraint.rb 39
lib/chef/provider/group.rb 40
lib/chef/provider/remote_file.rb 40
lib/chef/monkey_patches/numeric.rb 40
lib/chef/knife.rb 40
lib/chef/node.rb 41
lib/chef/provider/remote_directory.rb 41
lib/chef/role.rb 41
lib/chef/provider/mount.rb 41
lib/chef/provider/env.rb 41
lib/chef/run_list/run_list_item.rb 41
lib/chef/knife/core/text_formatter.rb 42
lib/chef/resource/deploy.rb 42
lib/chef/provider/mdadm.rb 42
lib/chef/provider/package/freebsd.rb 42
lib/chef/resource/service.rb 42
lib/chef/provider/user/windows.rb 42
lib/chef/provider/group/dscl.rb 42
lib/chef/mixin/deep_merge.rb 42
lib/chef/application.rb 43
lib/chef/provider/service.rb 43
lib/chef/solr_query/lucene_nodes.rb 43
lib/chef/provider/subversion.rb 43
lib/chef/cookbook/remote_file_vendor.rb 43
lib/chef/provider.rb 43
lib/chef/knife/cookbook_download.rb 43
lib/chef/runner.rb 43
lib/chef/solr_query.rb 43
lib/chef/resource/mdadm.rb 43
lib/chef/knife/cookbook_metadata.rb 44
lib/chef/resource/package.rb 44
lib/chef/knife/node_bulk_delete.rb 44
lib/chef/knife/cookbook_bulk_delete.rb 44
lib/chef/provider/mount/windows.rb 44
lib/chef/resource/group.rb 45
lib/chef/knife/core/ui.rb 45
lib/chef/resource/link.rb 45
lib/chef/knife/cookbook_site_install.rb 45
lib/chef/index_queue/amqp_client.rb 45
lib/chef/resource/erl_call.rb 45
lib/chef/openid_registration.rb 46
lib/chef/knife/client_bulk_delete.rb 46
lib/chef/cookbook/syntax_check.rb 46
lib/chef/shef/shef_session.rb 46
lib/chef/resource/directory.rb 46
lib/chef/provider/directory.rb 46
lib/chef/data_bag.rb 46
lib/chef/mixin/recipe_definition_dsl_core.rb 46
lib/chef/daemon.rb 46
lib/chef/checksum_cache.rb 47
lib/chef/knife/data_bag_create.rb 47
lib/chef/solr_query/solr_http_request.rb 47
lib/chef/data_bag_item.rb 47
lib/chef/mixin/command.rb 47
lib/chef/provider/group/windows.rb 47
lib/chef/sandbox.rb 48
lib/chef/webui_user.rb 48
lib/chef/provider/execute.rb 48
lib/chef/provider/cookbook_file.rb 48
lib/chef/resource/template.rb 48
lib/chef/knife/core/object_loader.rb 48
lib/chef/resource_collection/stepable_iterator.rb 48
lib/chef/index_queue/indexable.rb 48
lib/chef/provider/group/groupadd.rb 48
lib/chef/knife/core/bootstrap_context.rb 48
lib/chef/provider/template.rb 49
lib/chef/provider/file.rb 49
lib/chef/provider/group/usermod.rb 49
lib/chef/run_context.rb 49
lib/chef/provider/package/rubygems.rb 49
lib/chef/provider/service/solaris.rb 49
lib/chef/knife/role_bulk_delete.rb 49
lib/chef/provider/group/aix.rb 49
lib/chef/knife/core/node_presenter.rb 49
lib/chef/client.rb 50
lib/chef/provider/group/pw.rb 50
lib/chef/monkey_patches/tempfile.rb 50
lib/chef/cookbook_loader.rb 50
lib/chef/util/file_edit.rb 50
lib/chef/file_cache.rb 50
lib/chef/resource/env.rb 50
lib/chef/shef.rb 51
lib/chef/resource/http_request.rb 51
lib/chef/knife/ssh.rb 51
lib/chef/provider/service/init.rb 51
lib/chef/knife/configure_client.rb 52
lib/chef/knife/tag_delete.rb 52
lib/chef/resource/remote_file.rb 52
lib/chef/knife/cookbook_show.rb 52
lib/chef/mixin/template.rb 52
lib/chef/resource/yum_package.rb 52
lib/chef/resource/script.rb 52
lib/chef/mixin/language_include_attribute.rb 52
lib/chef/file_access_control.rb 52
lib/chef/mixin/language.rb 52
lib/chef/api_client.rb 52
lib/chef/provider/service/gentoo.rb 53
lib/chef/knife/core/node_editor.rb 53
lib/chef/mixin/create_path.rb 54
lib/chef/resource/easy_install_package.rb 54
lib/chef/knife/node_run_list_add.rb 54
lib/chef/knife/data_bag_edit.rb 54
lib/chef/version_class.rb 54
lib/chef/mixin/language_include_recipe.rb 54
lib/chef/knife/search.rb 54
lib/chef/encrypted_data_bag_item.rb 54
lib/chef/knife/cookbook_site_unshare.rb 55
lib/chef/rest/auth_credentials.rb 55
lib/chef/shef/model_wrapper.rb 55
lib/chef/handler/json_file.rb 55
lib/chef/cookbook/metadata.rb 55
lib/chef/knife/client_create.rb 56
lib/chef/knife/data_bag_show.rb 56
lib/chef/monkey_patches/dir.rb 56
lib/chef/application/client.rb 56
lib/chef/provider/env/windows.rb 56
lib/chef/knife/bootstrap.rb 56
lib/chef/application/solo.rb 56
lib/chef/knife/cookbook_site_download.rb 57
lib/chef/index_queue/consumer.rb 57
lib/chef/run_list/versioned_recipe_list.rb 57
lib/chef/provider/group/gpasswd.rb 58
lib/chef/resource_definition.rb 58
lib/chef/provider/group/suse.rb 58
lib/chef/cookbook/chefignore.rb 58
lib/chef/provider/script.rb 58
lib/chef/run_list.rb 58
lib/chef/knife/client_reregister.rb 59
lib/chef/checksum.rb 59
lib/chef/knife/cookbook_site_show.rb 60
lib/chef/knife/tag_create.rb 60
lib/chef/knife/data_bag_from_file.rb 60
lib/chef/mixin/convert_to_class_name.rb 60
lib/chef/provider/deploy/revision.rb 61
lib/chef/run_list/run_list_expansion.rb 61
lib/chef/provider/service/insserv.rb 62
lib/chef/provider/service/redhat.rb 62
lib/chef/knife/node_run_list_remove.rb 63
lib/chef/knife/data_bag_delete.rb 63
lib/chef/knife/cookbook_site_list.rb 63
lib/chef/solr_query/query_transform.rb 63
lib/chef/resource/ohai.rb 63
lib/chef/knife/exec.rb 63
lib/chef/knife/client_delete.rb 63
lib/chef/knife/environment_show.rb 63
lib/chef/provider/ohai.rb 64
lib/chef/knife/environment_create.rb 64
lib/chef/knife/environment_delete.rb 64
lib/chef/knife/environment_edit.rb 64
lib/chef/knife/role_delete.rb 64
lib/chef/knife/node_delete.rb 64
lib/chef/knife/node_create.rb 64
lib/chef/knife/client_edit.rb 64
lib/chef/knife/client_show.rb 65
lib/chef/search/query.rb 65
lib/chef/resource/ruby_block.rb 65
lib/chef/knife/role_create.rb 65
lib/chef/knife/tag_list.rb 66
lib/chef/mixin/shell_out.rb 66
lib/chef/knife/environment_from_file.rb 66
lib/chef/mixin/deprecation.rb 66
lib/chef/shell_out.rb 66
lib/chef/knife/cookbook_site_search.rb 67
lib/chef/knife/role_edit.rb 67
lib/chef/knife/role_show.rb 67
lib/chef/resource/cookbook_file.rb 67
lib/chef/mixin/check_helper.rb 68
lib/chef/run_status.rb 68
lib/chef/knife/index_rebuild.rb 68
lib/chef/mixin/from_file.rb 68
lib/chef/recipe.rb 69
lib/chef/resource/subversion.rb 69
lib/chef/knife/node_edit.rb 69
lib/chef/cookbook/file_system_file_vendor.rb 70
lib/chef/resource/deploy_revision.rb 70
lib/chef/knife/node_show.rb 71
lib/chef/json_compat.rb 71
lib/chef/provider/breakpoint.rb 72
lib/chef/resource/gem_package.rb 72
lib/chef/knife/node_from_file.rb 72
lib/chef/knife/cookbook_metadata_from_file.rb 73
lib/chef/monkey_patches/string.rb 73
lib/chef/knife/role_from_file.rb 74
lib/chef/application/knife.rb 74
lib/chef/mixin/get_source_from_package.rb 74
lib/chef/provider/ruby_block.rb 74
lib/chef/resource/breakpoint.rb 74
lib/chef/knife/recipe_list.rb 75
lib/chef/mash.rb 76
lib/chef/resource/perl.rb 76
lib/chef/resource/log.rb 76
lib/chef/resource/dpkg_package.rb 76
lib/chef/resource/csh.rb 76
lib/chef/provider/log.rb 76
lib/chef/resource/bash.rb 76
lib/chef/resource/rpm_package.rb 76
lib/chef/resource/portage_package.rb 76
lib/chef/resource/macports_package.rb 76
lib/chef/resource/python.rb 76
lib/chef/resource/ruby.rb 76
lib/chef/resource/apt_package.rb 76
lib/chef/resource/git.rb 76
lib/chef/resource/pacman_package.rb 76
lib/chef/resource/freebsd_package.rb 77
lib/chef/cookbook/file_vendor.rb 77
lib/chef/knife/node_list.rb 78
lib/chef/provider/deploy/timestamped.rb 79
lib/chef/handler/error_report.rb 79
lib/chef/resource_definition_list.rb 79
lib/chef/handler.rb 80
lib/chef/provider/service/invokercd.rb 80
lib/chef/exceptions.rb 80
lib/chef/cookbook/cookbook_collection.rb 80
lib/chef/resource/timestamped_deploy.rb 81
lib/chef/knife/client_list.rb 81
lib/chef/knife/role_list.rb 81
lib/chef/knife/cookbook_list.rb 81
lib/chef/knife/environment_list.rb 81
lib/chef/mixin/checksum.rb 81
lib/chef/knife/data_bag_list.rb 85
lib/chef/log.rb 85
lib/chef/knife/cookbook_site_vendor.rb 87
lib/chef/mixin/xml_escape.rb 88
lib/chef/config.rb 90
lib/chef/knife/core/subcommand_loader.rb 97
lib/chef/rest/cookie_jar.rb 100
lib/chef/mixins.rb 100
lib/chef/shef/shef_rest.rb 100
lib/chef/providers.rb 100
lib/chef.rb 100
lib/chef/resources.rb 100
lib/chef/nil_argument.rb 100
lib/chef/applications.rb 100
lib/chef/knife/help_topics.rb 100
lib/chef/index_queue.rb 100
lib/chef/application/agent.rb 100

lib/chef/mixin/command/unix.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module Mixin
       module Command
         module Unix
           # This is taken directly from Ara T Howard's Open4 library, and then
           # modified to suit the needs of Chef.  Any bugs here are most likely
           # my own, and not Ara's.
           #
           # The original appears in external/open4.rb in its unmodified form.
           #
           # Thanks Ara!
           def popen4(cmd, args={}, &b)
   
             # Waitlast - this is magic.
             #
             # Do we wait for the child process to die before we yield
             # to the block, or after?  That is the magic of waitlast.
             #
             # By default, we are waiting before we yield the block.
             args[:waitlast] ||= false
   
             args[:user] ||= nil
             unless args[:user].kind_of?(Integer)
               args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
             end
             args[:group] ||= nil
             unless args[:group].kind_of?(Integer)
               args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
             end
             args[:environment] ||= {}
   
             # Default on C locale so parsing commands output can be done
             # independently of the node's default locale.
             # "LC_ALL" could be set to nil, in which case we also must ignore it.
             unless args[:environment].has_key?("LC_ALL")
               args[:environment]["LC_ALL"] = "C"
             end
   
             pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
   
             verbose = $VERBOSE
             begin
               $VERBOSE = nil
               ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
   
               cid = fork {
                 pw.last.close
                 STDIN.reopen pw.first
                 pw.first.close
   
                 pr.first.close
                 STDOUT.reopen pr.last
                 pr.last.close
   
                 pe.first.close
                 STDERR.reopen pe.last
                 pe.last.close
   
                 STDOUT.sync = STDERR.sync = true
   
                 if args[:group]
                   Process.egid = args[:group]
                   Process.gid = args[:group]
                 end
   
                 if args[:user]
                   Process.euid = args[:user]
                   Process.uid = args[:user]
                 end
   
                 args[:environment].each do |key,value|
                   ENV[key] = value
                 end
   
                 if args[:umask]
                   umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
                   File.umask(umask)
                 end
   
                 begin
                   if cmd.kind_of?(Array)
                     exec(*cmd)
                   else
                     exec(cmd)
                   end
                   raise 'forty-two'
                 rescue Exception => e
                   Marshal.dump(e, ps.last)
                   ps.last.flush
                 end
                 ps.last.close unless (ps.last.closed?)
                 exit!
               }
             ensure
               $VERBOSE = verbose
             end
   
             [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
   
             begin
               e = Marshal.load ps.first
               raise(Exception === e ? e : "unknown failure!")
             rescue EOFError # If we get an EOF error, then the exec was successful
               42
             ensure
               ps.first.close
             end
   
             pw.last.sync = true
   
             pi = [pw.last, pr.first, pe.first]
   
             if b
               begin
                 if args[:waitlast]
                   b[cid, *pi]
                   # send EOF so that if the child process is reading from STDIN
                   # it will actually finish up and exit
                   pi[0].close_write
                   Process.waitpid2(cid).last
                 else
                   # This took some doing.
                   # The trick here is to close STDIN
                   # Then set our end of the childs pipes to be O_NONBLOCK
                   # Then wait for the child to die, which means any IO it
                   # wants to do must be done - it's dead.  If it isn't,
                   # it's because something totally skanky is happening,
                   # and we don't care.
                   o = StringIO.new
                   e = StringIO.new
   
                   pi[0].close
   
                   stdout = pi[1]
                   stderr = pi[2]
   
                   stdout.sync = true
                   stderr.sync = true
   
                   stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
                   stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
   
                   stdout_finished = false
                   stderr_finished = false
   
                   results = nil
   
                   while !stdout_finished || !stderr_finished
                     begin
                       channels_to_watch = []
                       channels_to_watch << stdout if !stdout_finished
                       channels_to_watch << stderr if !stderr_finished
                       ready = IO.select(channels_to_watch, nil, nil, 1.0)
                     rescue Errno::EAGAIN
                     ensure
                       results = Process.waitpid2(cid, Process::WNOHANG)
                       if results
                         stdout_finished = true
                         stderr_finished = true
                       end
                     end
   
                     if ready && ready.first.include?(stdout)
                       line = results ? stdout.gets(nil) : stdout.gets
                       if line
                         o.write(line)
                       else
                         stdout_finished = true
                       end
                     end
                     if ready && ready.first.include?(stderr)
                       line = results ? stderr.gets(nil) : stderr.gets
                       if line
                         e.write(line)
                       else
                         stderr_finished = true
                       end
                     end
                   end
                   results = Process.waitpid2(cid) unless results
                   o.rewind
                   e.rewind
                   b[cid, pi[0], o, e]
                   results.last
                 end
               ensure
                 pi.each{|fd| fd.close unless fd.closed?}
               end
             else
               [cid, pw.last, pr.first, pe.first]
             end
           end
   
         end
       end
     end
   end

lib/chef/platform.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/log'
   require 'chef/mixin/params_validate'
   
   # Actually, this file depends on nearly every provider in chef, but actually
   # requiring them causes circular requires resulting in uninitialized constant
   # errors.
   require 'chef/provider'
   require 'chef/provider/log'
   require 'chef/provider/user'
   require 'chef/provider/group'
   require 'chef/provider/mount'
   require 'chef/provider/service'
   require 'chef/provider/package'
   
   
   class Chef
     class Platform
   
       class << self
         attr_writer :platforms
   
         def platforms
           @platforms ||= {
             :mac_os_x => {
               :default => {
                 :package => Chef::Provider::Package::Macports,
                 :user => Chef::Provider::User::Dscl,
                 :group => Chef::Provider::Group::Dscl
               }
             },
             :mac_os_x_server => {
               :default => {
                 :package => Chef::Provider::Package::Macports,
                 :user => Chef::Provider::User::Dscl,
                 :group => Chef::Provider::Group::Dscl
               }
             },
             :freebsd => {
               :default => {
                 :group   => Chef::Provider::Group::Pw,
                 :package => Chef::Provider::Package::Freebsd,
                 :service => Chef::Provider::Service::Freebsd,
                 :user    => Chef::Provider::User::Pw,
                 :cron    => Chef::Provider::Cron
               }
             },
             :ubuntu   => {
               :default => {
                 :package => Chef::Provider::Package::Apt,
                 :service => Chef::Provider::Service::Debian,
                 :cron => Chef::Provider::Cron,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :debian => {
               :default => {
                 :package => Chef::Provider::Package::Apt,
                 :service => Chef::Provider::Service::Debian,
                 :cron => Chef::Provider::Cron,
                 :mdadm => Chef::Provider::Mdadm
               },
               "6.0" => {
                 :service => Chef::Provider::Service::Insserv
               }
             },
             :xenserver   => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :centos   => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :amazon   => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :scientific => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :fedora   => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :suse     => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Zypper,
                 :group => Chef::Provider::Group::Suse
               }
             },
             :redhat   => {
               :default => {
                 :service => Chef::Provider::Service::Redhat,
                 :cron => Chef::Provider::Cron,
                 :package => Chef::Provider::Package::Yum,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :gentoo   => {
               :default => {
                 :package => Chef::Provider::Package::Portage,
                 :service => Chef::Provider::Service::Gentoo,
                 :cron => Chef::Provider::Cron,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :arch   => {
               :default => {
                 :package => Chef::Provider::Package::Pacman,
                 :service => Chef::Provider::Service::Arch,
                 :cron => Chef::Provider::Cron,
                 :mdadm => Chef::Provider::Mdadm
               }
             },
             :mswin => {
               :default => {
                 :env =>  Chef::Provider::Env::Windows,
                 :service => Chef::Provider::Service::Windows,
                 :user => Chef::Provider::User::Windows,
                 :group => Chef::Provider::Group::Windows,
                 :mount => Chef::Provider::Mount::Windows
               }
             },
             :mingw32 => {
               :default => {
                 :env =>  Chef::Provider::Env::Windows,
                 :service => Chef::Provider::Service::Windows,
                 :user => Chef::Provider::User::Windows,
                 :group => Chef::Provider::Group::Windows,
                 :mount => Chef::Provider::Mount::Windows
               }
             },
             :windows => {
               :default => {
                 :env =>  Chef::Provider::Env::Windows,
                 :service => Chef::Provider::Service::Windows,
                 :user => Chef::Provider::User::Windows,
                 :group => Chef::Provider::Group::Windows,
                 :mount => Chef::Provider::Mount::Windows
               }
             },
             :solaris  => {},
             :openindiana => {
               :default => {
                 :service => Chef::Provider::Service::Solaris,
                 :package => Chef::Provider::Package::Solaris,
                 :cron => Chef::Provider::Cron::Solaris,
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :opensolaris => {
               :default => {
                 :service => Chef::Provider::Service::Solaris,
                 :package => Chef::Provider::Package::Solaris,
                 :cron => Chef::Provider::Cron::Solaris,
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :nexentacore => {
               :default => {
                 :service => Chef::Provider::Service::Solaris,
                 :package => Chef::Provider::Package::Solaris,
                 :cron => Chef::Provider::Cron::Solaris,
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :solaris2 => {
               :default => {
                 :service => Chef::Provider::Service::Solaris,
                 :package => Chef::Provider::Package::Solaris,
                 :cron => Chef::Provider::Cron::Solaris,
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :netbsd => {
               :default => {
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :openbsd => {
               :default => {
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :hpux => {
               :default => {
                 :group => Chef::Provider::Group::Usermod
               }
             },
             :aix => {
               :default => {
                 :group => Chef::Provider::Group::Aix
               }
             },
             :default  => {
               :file => Chef::Provider::File,
               :directory => Chef::Provider::Directory,
               :link => Chef::Provider::Link,
               :template => Chef::Provider::Template,
               :remote_directory => Chef::Provider::RemoteDirectory,
               :execute => Chef::Provider::Execute,
               :mount => Chef::Provider::Mount::Mount,
               :script => Chef::Provider::Script,
               :service => Chef::Provider::Service::Init,
               :perl => Chef::Provider::Script,
               :python => Chef::Provider::Script,
               :ruby => Chef::Provider::Script,
               :bash => Chef::Provider::Script,
               :csh => Chef::Provider::Script,
               :user => Chef::Provider::User::Useradd,
               :group => Chef::Provider::Group::Gpasswd,
               :http_request => Chef::Provider::HttpRequest,
               :route => Chef::Provider::Route,
               :ifconfig => Chef::Provider::Ifconfig,
               :ruby_block => Chef::Provider::RubyBlock,
               :erl_call => Chef::Provider::ErlCall,
               :log => Chef::Provider::Log::ChefLog
             }
           }
         end
   
         include Chef::Mixin::ParamsValidate
   
         def find(name, version)
           provider_map = platforms[:default].clone
   
           name_sym = name
           if name.kind_of?(String)
             name.downcase!
             name.gsub!(/\s/, "_")
             name_sym = name.to_sym
           end
   
           if platforms.has_key?(name_sym)
             if platforms[name_sym].has_key?(version)
               Chef::Log.debug("Platform #{name.to_s} version #{version} found")
               if platforms[name_sym].has_key?(:default)
                 provider_map.merge!(platforms[name_sym][:default])
               end
               provider_map.merge!(platforms[name_sym][version])
             elsif platforms[name_sym].has_key?(:default)
               provider_map.merge!(platforms[name_sym][:default])
             end
           else
             Chef::Log.debug("Platform #{name} not found, using all defaults. (Unsupported platform?)")
           end
           provider_map
         end
   
         def find_platform_and_version(node)
           platform = nil
           version = nil
   
           if node[:platform]
             platform = node[:platform]
           elsif node.attribute?("os")
             platform = node[:os]
           end
   
           raise ArgumentError, "Cannot find a platform for #{node}" unless platform
   
           if node[:platform_version]
             version = node[:platform_version]
           elsif node[:os_version]
             version = node[:os_version]
           elsif node[:os_release]
             version = node[:os_release]
           end
   
           raise ArgumentError, "Cannot find a version for #{node}" unless version
   
           return platform, version
         end
   
         def provider_for_resource(resource)
           node = resource.run_context && resource.run_context.node
           raise ArgumentError, "Cannot find the provider for a resource with no run context set" unless node
           find_provider_for_node(node, resource).new(resource, resource.run_context)
         end
   
         def provider_for_node(node, resource_type)
           raise NotImplementedError, "#{self.class.name} no longer supports #provider_for_node"
           find_provider_for_node(node, resource_type).new(node, resource_type)
         end
   
         def find_provider_for_node(node, resource_type)
           platform, version = find_platform_and_version(node)
           provider = find_provider(platform, version, resource_type)
         end
   
         def set(args)
           validate(
             args,
             {
               :platform => {
                 :kind_of => Symbol,
                 :required => false,
               },
               :version => {
                 :kind_of => String,
                 :required => false,
               },
               :resource => {
                 :kind_of => Symbol,
               },
               :provider => {
                 :kind_of => [ String, Symbol, Class ],
               }
             }
           )
           if args.has_key?(:platform)
             if args.has_key?(:version)
               if platforms.has_key?(args[:platform])
                 if platforms[args[:platform]].has_key?(args[:version])
                   platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider]
                 else
                   platforms[args[:platform]][args[:version]] = {
                     args[:resource].to_sym => args[:provider]
                   }
                 end
               else
                 platforms[args[:platform]] = {
                   args[:version] => {
                     args[:resource].to_sym => args[:provider]
                   }
                 }
               end
             else
               if platforms.has_key?(args[:platform])
                 if platforms[args[:platform]].has_key?(:default)
                   platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
                 else
                   platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
                 end
               else
                 platforms[args[:platform]] = {
                   :default => {
                     args[:resource].to_sym => args[:provider]
                   }
                 }
               end
             end
           else
             if platforms.has_key?(:default)
               platforms[:default][args[:resource].to_sym] = args[:provider]
             else
               platforms[:default] = {
                 args[:resource].to_sym => args[:provider]
               }
             end
           end
         end
   
         def find_provider(platform, version, resource_type)
           pmap = Chef::Platform.find(platform, version)
           provider_klass = explicit_provider(platform, version, resource_type) ||
                            platform_provider(platform, version, resource_type) ||
                            resource_matching_provider(platform, version, resource_type)
   
           raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
   
           provider_klass
         end
   
         private
   
           def explicit_provider(platform, version, resource_type)
             resource_type.kind_of?(Chef::Resource) ? resource_type.provider : nil
           end
   
           def platform_provider(platform, version, resource_type)
             pmap = Chef::Platform.find(platform, version)
             rtkey = resource_type.kind_of?(Chef::Resource) ? resource_type.resource_name.to_sym : resource_type
             pmap.has_key?(rtkey) ? pmap[rtkey] : nil
           end
   
           def resource_matching_provider(platform, version, resource_type)
             if resource_type.kind_of?(Chef::Resource)
               begin
                 Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
               rescue NameError
                 nil
               end
             else
               nil
             end
           end
   
       end
   
     end
   end

lib/chef/provider/package/macports.rb

   class Chef
     class Provider
       class Package
         class Macports < Chef::Provider::Package
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
   
             @current_resource.version(current_installed_version)
             Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version
   
             @candidate_version = macports_candidate_version
   
             if !@new_resource.version and !@candidate_version
               raise Chef::Exceptions::Package, "Could not get a candidate version for this package -- #{@new_resource.name} does not seem to be a valid package!"
             end
   
             Chef::Log.debug("#{@new_resource} candidate version is #{@candidate_version}") if @candidate_version
   
             @current_resource
           end
   
           def current_installed_version
             command = "port installed #{@new_resource.package_name}"
             output = get_response_from_command(command)
   
             response = nil
             output.each_line do |line|
               match = line.match(/^.+ @([^\s]+) \(active\)$/)
               response = match[1] if match
             end
             response
           end
   
           def macports_candidate_version
             command = "port info --version #{@new_resource.package_name}"
             output = get_response_from_command(command)
   
             match = output.match(/^version: (.+)$/)
   
             match ? match[1] : nil
           end
   
           def install_package(name, version)
             unless @current_resource.version == version
               command = "port#{expand_options(@new_resource.options)} install #{name}"
               command << " @#{version}" if version and !version.empty? 
               run_command_with_systems_locale(
                 :command => command
               )
             end
           end
   
           def purge_package(name, version)
             command = "port#{expand_options(@new_resource.options)} uninstall #{name}"
             command << " @#{version}" if version and !version.empty?
             run_command_with_systems_locale(
               :command => command
             )
           end
   
           def remove_package(name, version)
             command = "port#{expand_options(@new_resource.options)} deactivate #{name}"
             command << " @#{version}" if version and !version.empty?
   
             run_command_with_systems_locale(
               :command => command
             )
           end
   
           def upgrade_package(name, version)
             # Saving this to a variable -- weird rSpec behavior
             # happens otherwise...
             current_version = @current_resource.version
   
             if current_version.nil? or current_version.empty?
               # Macports doesn't like when you upgrade a package
               # that hasn't been installed.
               install_package(name, version)
             elsif current_version != version
               run_command_with_systems_locale(
                 :command => "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}"
               )
             end
           end
   
           private
           def get_response_from_command(command)
             output = nil
             status = popen4(command) do |pid, stdin, stdout, stderr|
               begin
                 output = stdout.read
               rescue Exception
                 raise Chef::Exceptions::Package, "Could not read from STDOUT on command: #{command}"
               end
             end
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "#{command} failed - #{status.insect}!"
             end
             output
           end
         end
       end
     end
   end

lib/chef/shef/ext.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'tempfile'
   require 'chef/recipe'
   require 'fileutils'
   require 'chef/version'
   require 'chef/shef/shef_session'
   require 'chef/shef/model_wrapper'
   require 'chef/shef/shef_rest'
   require 'chef/json_compat'
   
   module Shef
     module Extensions
   
       Help = Struct.new(:cmd, :desc, :explanation)
   
       # Extensions to be included in every 'main' object in shef. These objects
       # are extended with this module.
       module ObjectCoreExtensions
   
         def ensure_session_select_defined
           # irb breaks if you prematurely define IRB::JobMangager
           # so these methods need to be defined at the latest possible time.
           unless jobs.respond_to?(:select_session_by_context)
             def jobs.select_session_by_context(&block)
               @jobs.select { |job| block.call(job[1].context.main)}
             end
           end
   
           unless jobs.respond_to?(:session_select)
             def jobs.select_shef_session(target_context)
               session = if target_context.kind_of?(Class)
                 select_session_by_context { |main| main.kind_of?(target_context) }
               else
                 select_session_by_context { |main| main.equal?(target_context) }
               end
               Array(session.first)[1]
             end
           end
         end
   
         def find_or_create_session_for(context_obj)
           ensure_session_select_defined
           if subsession = jobs.select_shef_session(context_obj)
             jobs.switch(subsession)
           else
             irb(context_obj)
           end
         end
   
         def help_banner
           banner = []
           banner << ""
           banner << "Shef Help"
           banner << "".ljust(80, "=")
           banner << "| " + "Command".ljust(25) + "| " + "Description"
           banner << "".ljust(80, "=")
   
           self.all_help_descriptions.each do |help_text|
             banner << "| " + help_text.cmd.ljust(25) + "| " + help_text.desc
           end
           banner << "".ljust(80, "=")
           banner << "\n"
           banner << "Use help(:command) to get detailed help with individual commands"
           banner << "\n"
           banner.join("\n")
         end
   
         def explain_command(method_name)
           help = self.all_help_descriptions.find { |h| h.cmd.to_s == method_name.to_s }
           if help
             puts ""
             puts "Command: #{method_name}"
             puts "".ljust(80, "=")
             puts help.explanation || help.desc
             puts "".ljust(80, "=")
             puts ""
           else
             puts ""
             puts "command #{method_name} not found or no help available"
             puts ""
           end
         end
   
         # helpfully returns +:on+ so we can have sugary syntax like `tracing on'
         def on
           :on
         end
   
         # returns +:off+ so you can just do `tracing off'
         def off
           :off
         end
   
         def help_descriptions
           @help_descriptions ||= []
         end
   
         def all_help_descriptions
           help_descriptions
         end
   
         def desc(help_text)
           @desc = help_text
         end
   
         def explain(explain_text)
           @explain = explain_text
         end
   
         def subcommands(subcommand_help={})
           @subcommand_help = subcommand_help
         end
   
         def singleton_method_added(mname)
           if @desc
             help_descriptions << Help.new(mname.to_s, @desc.to_s, @explain)
             @desc, @explain = nil, nil
           end
           if @subcommand_help
             @subcommand_help.each do |subcommand, text|
               help_descriptions << Help.new("#{mname}.#{subcommand}", text.to_s, nil)
             end
           end
           @subcommand_help = {}
         end
   
       end
   
       module String
         def on_off_to_bool
           case self
           when "on"
             true
           when "off"
             false
           else
             self
           end
         end
       end
   
       module Symbol
         def on_off_to_bool
           self.to_s.on_off_to_bool
         end
       end
   
       module TrueClass
         def to_on_off_str
           "on"
         end
   
         def on_off_to_bool
           self
         end
       end
   
       module FalseClass
         def to_on_off_str
           "off"
         end
   
         def on_off_to_bool
           self
         end
       end
   
       # Methods that have associated help text need to be dynamically added
       # to the main irb objects, so we define them in a proc and later
       # instance_eval the proc in the object.
       ObjectUIExtensions = Proc.new do
         extend Shef::Extensions::ObjectCoreExtensions
   
         desc "prints this help message"
         explain(<<-E)
   ## SUMMARY ##
     When called with no argument, +help+ prints a table of all shef commands. When
     called with an argument COMMAND, +help+ prints a detailed explanation of the
     command if available, or the description if no explanation is available.
   E
         def help(commmand=nil)
           if commmand
             explain_command(commmand)
           else
             puts help_banner
           end
           :ucanhaz_halp
         end
         alias :halp :help
   
         desc "prints information about chef"
         def version
           puts  "This is shef, the Chef shell.\n" +
                 " Chef Version: #{::Chef::VERSION}\n" +
                 " http://www.opscode.com/chef\n" +
                 " http://wiki.opscode.com/display/chef/Home"
           :ucanhaz_automation
         end
         alias :shef :version
   
         desc "switch to recipe mode"
         def recipe
           find_or_create_session_for Shef.session.recipe
           :recipe
         end
   
         desc "switch to attributes mode"
         def attributes
           find_or_create_session_for Shef.session.node
           :attributes
         end
   
         desc "run chef using the current recipe"
         def run_chef
           Chef::Log.level = :debug
           session = Shef.session
           runrun = Chef::Runner.new(session.run_context).converge
           Chef::Log.level = :info
           runrun
         end
   
         desc "returns an object to control a paused chef run"
         subcommands :resume       => "resume the chef run",
                     :step         => "run only the next resource",
                     :skip_back    => "move back in the run list",
                     :skip_forward => "move forward in the run list"
         def chef_run
           Shef.session.resource_collection.iterator
         end
   
         desc "resets the current recipe"
         def reset
           Shef.session.reset!
         end
   
         desc "assume the identity of another node."
         def become_node(node_name)
           Shef::DoppelGangerSession.instance.assume_identity(node_name)
           :doppelganger
         end
         alias :doppelganger :become_node
   
         desc "turns printout of return values on or off"
         def echo(on_or_off)
           conf.echo = on_or_off.on_off_to_bool
         end
   
         desc "says if echo is on or off"
         def echo?
           puts "echo is #{conf.echo.to_on_off_str}"
         end
   
         desc "turns on or off tracing of execution. *verbose*"
         def tracing(on_or_off)
           conf.use_tracer = on_or_off.on_off_to_bool
           tracing?
         end
         alias :trace :tracing
   
         desc "says if tracing is on or off"
         def tracing?
           puts "tracing is #{conf.use_tracer.to_on_off_str}"
         end
         alias :trace? :tracing?
   
         desc "simple ls style command"
         def ls(directory)
           Dir.entries(directory)
         end
       end
   
       MainContextExtensions = Proc.new do
         desc "returns the current node (i.e., this host)"
         def node
           Shef.session.node
         end
   
         desc "pretty print the node's attributes"
         def ohai(key=nil)
           pp(key ? node.attribute[key] : node.attribute)
         end
       end
   
       RESTApiExtensions = Proc.new do
         desc "edit an object in your EDITOR"
         explain(<<-E)
   ## SUMMARY ##
     +edit(object)+ allows you to edit any object that can be converted to JSON.
     When finished editing, this method will return the edited object:
   
         new_node = edit(existing_node)
   
   ## EDITOR SELECTION ##
     Shef looks for an editor using the following logic
     1. Looks for an EDITOR set by Shef.editor = "EDITOR"
     2. Looks for an EDITOR configured in your shef config file
     3. Uses the value of the EDITOR environment variable
   E
         def edit(object)
           unless Shef.editor
             puts "Please set your editor with Shef.editor = \"vim|emacs|mate|ed\""
             return :failburger
           end
   
           filename = "shef-edit-#{object.class.name}-"
           if object.respond_to?(:name)
             filename += object.name
           elsif object.respond_to?(:id)
             filename += object.id
           end
   
           edited_data = Tempfile.open([filename, ".js"]) do |tempfile|
             tempfile.sync = true
             tempfile.puts Chef::JSONCompat.to_json(object)
             system("#{Shef.editor.to_s} #{tempfile.path}")
             tempfile.rewind
             tempfile.read
           end
   
           Chef::JSONCompat.from_json(edited_data)
         end
   
         desc "Find and edit API clients"
         explain(<<-E)
   ## SUMMARY ##
     +clients+ allows you to query you chef server for information about your api
     clients.
   
   ## LIST ALL CLIENTS ##
     To see all clients on the system, use
   
         clients.all #=> [, ...]
   
     If the output from all is too verbose, or you're only interested in a specific
     value from each of the objects, you can give a code block to +all+:
   
         clients.all { |client| client.name } #=> [CLIENT1_NAME, CLIENT2_NAME, ...]
   
   ## SHOW ONE CLIENT ##
     To see a specific client, use
   
         clients.show(CLIENT_NAME)
   
   ## SEARCH FOR CLIENTS ##
     You can also search for clients using +find+ or +search+. You can use the
     familiar string search syntax:
   
         clients.search("KEY:VALUE")
   
     Just as the +all+ subcommand, the +search+ subcommand can use a code block to
     filter or transform the information returned from the search:
   
         clients.search("KEY:VALUE") { |c| c.name }
   
     You can also use a Hash based syntax, multiple search conditions will be 
     joined with AND.
   
         clients.find :KEY => :VALUE, :KEY2 => :VALUE2, ...
   
   ## BULK-EDIT CLIENTS ##
                       **BE CAREFUL, THIS IS DESTRUCTIVE**
     You can bulk edit API Clients using the +transform+ subcommand, which requires
     a code block. Each client will be saved after the code block is run. If the
     code block returns +nil+ or +false+, that client will be skipped:
   
         clients.transform("*:*") do |client|
           if client.name =~ /borat/i
             client.admin(false)
             true
           else
             nil
           end
         end
   
     This will strip the admin privileges from any client named after borat.
   E
         subcommands :all        => "list all api clients",
                     :show       => "load an api client by name",
                     :search     => "search for API clients",
                     :transform  => "edit all api clients via a code block and save them"
         def clients
           @clients ||= Shef::ModelWrapper.new(Chef::ApiClient, :client)
         end
   
         desc "Find and edit cookbooks"
         subcommands :all        => "list all cookbooks",
                     :show       => "load a cookbook by name",
                     :transform  => "edit all cookbooks via a code block and save them"
         def cookbooks
           @cookbooks ||= Shef::ModelWrapper.new(Chef::CookbookVersion)
         end
   
         desc "Find and edit nodes via the API"
         explain(<<-E)
   ## SUMMARY ##
     +nodes+ Allows you to query your chef server for information about your nodes.
   
   ## LIST ALL NODES ##
     You can list all nodes using +all+ or +list+
   
         nodes.all #=> [, , ...]
   
     To limit the information returned for each node, pass a code block to the +all+
     subcommand:
   
         nodes.all { |node| node.name } #=> [NODE1_NAME, NODE2_NAME, ...]
   
   ## SHOW ONE NODE ##
     You can show the data for a single node using the +show+ subcommand:
   
         nodes.show("NODE_NAME") => 
   
   ## SEARCH FOR NODES ##
     You can search for nodes using the +search+ or +find+ subcommands:
   
         nodes.find(:name => "app*") #=> [, ...]
   
     Similarly to +all+, you can pass a code block to limit or transform the
     information returned:
   
         nodes.find(:name => "app#") { |node| node.ec2 } 
   
   ## BULK EDIT NODES ##
                 **BE CAREFUL, THIS OPERATION IS DESTRUCTIVE**
   
     Bulk edit nodes by passing a code block to the +transform+ or +bulk_edit+
     subcommand. The block will be applied to each matching node, and then the node
     will be saved. If the block returns +nil+ or +false+, that node will be 
     skipped.
   
         nodes.transform do |node|
           if node.fqdn =~ /.*\\.preprod\\.example\\.com/
             node.set[:environment] = "preprod"
           end
         end
   
     This will assign the attribute to every node with a FQDN matching the regex.
   E
         subcommands :all        => "list all nodes",
                     :show       => "load a node by name",
                     :search     => "search for nodes",
                     :transform  => "edit all nodes via a code block and save them"
         def nodes
           @nodes ||= Shef::ModelWrapper.new(Chef::Node)
         end
   
         desc "Find and edit roles via the API"
         explain(<<-E)
   ## SUMMARY ##
     +roles+ allows you to query and edit roles on your Chef server.
   
   ## SUBCOMMANDS ##
     * all       (list)
     * show      (load)
     * search    (find)
     * transform (bulk_edit)
   
   ## SEE ALSO ##
     See the help for +nodes+ for more information about the subcommands.
   E
         subcommands :all        => "list all roles",
                     :show       => "load a role by name",
                     :search     => "search for roles",
                     :transform  => "edit all roles via a code block and save them"
         def roles
           @roles ||= Shef::ModelWrapper.new(Chef::Role)
         end
   
         desc "Find and edit +databag_name+ via the api"
         explain(<<-E)
   ## SUMMARY ##
     +databags(DATABAG_NAME)+ allows you to query and edit data bag items on your
     Chef server. Unlike other commands for working with data on the server, 
     +databags+ requires the databag name as an argument, for example:
       databags(:users).all
   
   ## SUBCOMMANDS ##
     * all       (list)
     * show      (load)
     * search    (find)
     * transform (bulk_edit)
   
   ## SEE ALSO ##
     See the help for +nodes+ for more information about the subcommands.
   
   E
         subcommands :all        => "list all items in the data bag",
                     :show       => "load a data bag item by id",
                     :search     => "search for items in the data bag",
                     :transform  => "edit all items via a code block and save them"
         def databags(databag_name)
           @named_databags_wrappers ||= {}
           @named_databags_wrappers[databag_name] ||= Shef::NamedDataBagWrapper.new(databag_name)
         end
   
         desc "A REST Client configured to authenticate with the API"
         def api
           @rest = Shef::ShefREST.new(Chef::Config[:chef_server_url])
         end
   
       end
   
       RecipeUIExtensions = Proc.new do
         alias :original_resources :resources
   
         desc "list all the resources on the current recipe"
         def resources(*args)
           if args.empty?
             pp run_context.resource_collection.instance_variable_get(:@resources_by_name).keys
           else
             pp resources = original_resources(*args)
             resources
           end
         end
       end
   
       def self.extend_context_object(obj)
         obj.instance_eval(&ObjectUIExtensions)
         obj.instance_eval(&MainContextExtensions)
         obj.instance_eval(&RESTApiExtensions)
         obj.extend(FileUtils)
         obj.extend(Chef::Mixin::Language)
       end
   
       def self.extend_context_node(node_obj)
         node_obj.instance_eval(&ObjectUIExtensions)
       end
   
       def self.extend_context_recipe(recipe_obj)
         recipe_obj.instance_eval(&ObjectUIExtensions)
         recipe_obj.instance_eval(&RecipeUIExtensions)
       end
   
     end
   end
   
   class String
     include Shef::Extensions::String
   end
   
   class Symbol
     include Shef::Extensions::Symbol
   end
   
   class TrueClass
     include Shef::Extensions::TrueClass
   end
   
   class FalseClass
     include Shef::Extensions::FalseClass
   end
   

lib/chef/provider/cron.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Cron < Chef::Provider
         include Chef::Mixin::Command
   
         CRON_PATTERN = /([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s(.*)/
   
         def initialize(new_resource, run_context)
           super(new_resource, run_context)
           @cron_exists = false
           @cron_empty = false
         end
         attr_accessor :cron_exists, :cron_empty
   
         def load_current_resource
           crontab_lines = []
           @current_resource = Chef::Resource::Cron.new(@new_resource.name)
           @current_resource.user(@new_resource.user)
           status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
             stdout.each_line { |line| crontab_lines << line }
           end
           if status.exitstatus > 1
             raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}"
           elsif status.exitstatus == 0
             cron_found = false
             crontab_lines.each do |line|
               case line.chomp
               when "# Chef Name: #{@new_resource.name}"
                 Chef::Log.debug("Found cron '#{@new_resource.name}'")
                 cron_found = true
                 @cron_exists = true
                 next
               when /^MAILTO=(\S*)/
                 @current_resource.mailto($1) if cron_found
                 next
               when /^PATH=(\S*)/
                 @current_resource.path($1) if cron_found
                 next
               when /^SHELL=(\S*)/
                 @current_resource.shell($1) if cron_found
                 next
               when /^HOME=(\S*)/
                 @current_resource.home($1) if cron_found
                 next
               when CRON_PATTERN
                 if cron_found
                   @current_resource.minute($1)
                   @current_resource.hour($2)
                   @current_resource.day($3)
                   @current_resource.month($4)
                   @current_resource.weekday($5)
                   @current_resource.command($6)
                   cron_found=false
                 end
                 next
               else
                 next
               end
             end
             Chef::Log.debug("Cron '#{@new_resource.name}' not found") unless @cron_exists
           elsif status.exitstatus == 1
             Chef::Log.debug("Cron empty for '#{@new_resource.user}'")
             @cron_empty = true
           end
   
           @current_resource
         end
   
         def compare_cron
           [ :minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home ].any? do |cron_var|
             !@new_resource.send(cron_var).nil? && @new_resource.send(cron_var) != @current_resource.send(cron_var)
           end
         end
   
         def action_create
           crontab = String.new
           newcron = String.new
           cron_found = false
   
           newcron << "# Chef Name: #{new_resource.name}\n"
           [ :mailto, :path, :shell, :home ].each do |v|
             newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v)
           end
           newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
   
           if @cron_exists
             unless compare_cron
               Chef::Log.debug("Skipping existing cron entry '#{@new_resource.name}'")
               return
             end
             status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line.chomp
                 when "# Chef Name: #{@new_resource.name}"
                   cron_found = true
                   next
                 when CRON_PATTERN
                   if cron_found
                     cron_found = false
                     crontab << newcron
                     next
                   end
                 else
                   next if cron_found
                 end
                 crontab << line
               end
             end
   
             status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
               crontab.each_line { |line| stdin.puts "#{line}" }
             end
             Chef::Log.info("#{@new_resource} updated crontab entry")
             @new_resource.updated_by_last_action(true)
           else
             unless @cron_empty
               status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
                 stdout.each { |line| crontab << line }
               end
             end
   
             crontab << newcron
   
             status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
               crontab.each_line { |line| stdin.puts "#{line}" }
             end
             Chef::Log.info("#{@new_resource} added crontab entry")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def action_delete
           if @cron_exists
             crontab = String.new
             cron_found = false
             status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line.chomp
                 when "# Chef Name: #{@new_resource.name}"
                   cron_found = true
                   next
                 when CRON_PATTERN
                   if cron_found
                     cron_found = false
                     next
                   end
                 else
                   next if cron_found
                 end
                 crontab << line
               end
             end
   
             status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
               crontab.each_line { |line| stdin.puts "#{line}" }
             end
             Chef::Log.info("#{@new_resource} deleted crontab entry")
             @new_resource.updated_by_last_action(true)
           end
         end
   
       end
     end
   end

lib/chef/provider/package/yum.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'singleton'
   require 'chef/mixin/get_source_from_package'
   
   
   class Chef
     class Provider
       class Package
         class Yum < Chef::Provider::Package
   
           class RPMUtils
             class << self
   
               # RPM::Version version_parse equivalent
               def version_parse(evr)
                 return if evr.nil?
   
                 epoch = nil
                 # assume this is a version
                 version = evr
                 release = nil
   
                 lead = 0
                 tail = evr.size
   
                 if evr =~ %r{^([\d]+):}
                   epoch = $1.to_i
                   lead = $1.length + 1
                 elsif evr[0].ord == ":".ord
                   epoch = 0
                   lead = 1
                 end
   
                 if evr =~ %r{:?.*-(.*)$}
                   release = $1
                   tail = evr.length - release.length - lead - 1
   
                   if release.empty?
                     release = nil
                   end
                 end
   
                 version = evr[lead,tail]
                 if version.empty?
                   version = nil
                 end
   
                 [ epoch, version, release ]
               end
   
               # verify
               def isalnum(x)
                 isalpha(x) or isdigit(x)
               end
   
               def isalpha(x)
                 v = x.ord
                 (v >= 65 and v <= 90) or (v >= 97 and v <= 122)
               end
   
               def isdigit(x)
                 v = x.ord
                 v >= 48 and v <= 57
               end
   
               # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0
               def rpmvercmp(x, y)
                 # easy! :)
                 return 0 if x == y
   
                 if x.nil?
                   x = ""
                 end
   
                 if y.nil?
                   y = ""
                 end
   
                 # not so easy :(
                 #
                 # takes 2 strings like
                 #
                 # x = "1.20.b18.el5"
                 # y = "1.20.b17.el5"
                 #
                 # breaks into purely alpha and numeric segments and compares them using
                 # some rules
                 #
                 # * 10 > 1
                 # * 1 > a
                 # * z > a
                 # * Z > A
                 # * z > Z
                 # * leading zeros are ignored
                 # * separators (periods, commas) are ignored
                 # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5"
   
                 x_pos = 0                # overall string element reference position
                 x_pos_max = x.length - 1 # number of elements in string, starting from 0
                 x_seg_pos = 0            # segment string element reference position
                 x_comp = nil             # segment to compare
   
                 y_pos = 0
                 y_seg_pos = 0
                 y_pos_max = y.length - 1
                 y_comp = nil
   
                 while (x_pos <= x_pos_max and y_pos <= y_pos_max)
                   # first we skip over anything non alphanumeric
                   while (x_pos <= x_pos_max) and (isalnum(x[x_pos]) == false)
                     x_pos += 1 # +1 over pos_max if end of string
                   end
                   while (y_pos <= y_pos_max) and (isalnum(y[y_pos]) == false)
                     y_pos += 1
                   end
   
                   # if we hit the end of either we are done matching segments
                   if (x_pos == x_pos_max + 1) or (y_pos == y_pos_max + 1)
                     break
                   end
   
                   # we are now at the start of a alpha or numeric segment
                   x_seg_pos = x_pos
                   y_seg_pos = y_pos
   
                   # grab segment so we can compare them
                   if isdigit(x[x_seg_pos].ord)
                     x_seg_is_num = true
   
                     # already know it's a digit
                     x_seg_pos += 1
   
                     # gather up our digits
                     while (x_seg_pos <= x_pos_max) and isdigit(x[x_seg_pos])
                       x_seg_pos += 1
                     end
                     # copy the segment but not the unmatched character that x_seg_pos will
                     # refer to
                     x_comp = x[x_pos,x_seg_pos - x_pos]
   
                     while (y_seg_pos <= y_pos_max) and isdigit(y[y_seg_pos])
                       y_seg_pos += 1
                     end
                     y_comp = y[y_pos,y_seg_pos - y_pos]
                   else
                     # we are comparing strings
                     x_seg_is_num = false
   
                     while (x_seg_pos <= x_pos_max) and isalpha(x[x_seg_pos])
                       x_seg_pos += 1
                     end
                     x_comp = x[x_pos,x_seg_pos - x_pos]
   
                     while (y_seg_pos <= y_pos_max) and isalpha(y[y_seg_pos])
                       y_seg_pos += 1
                     end
                     y_comp = y[y_pos,y_seg_pos - y_pos]
                   end
   
                   # if y_seg_pos didn't advance in the above loop it means the segments are
                   # different types
                   if y_pos == y_seg_pos
                     # numbers always win over letters
                     return x_seg_is_num ? 1 : -1
                   end
   
                   # move the ball forward before we mess with the segments
                   x_pos += x_comp.length # +1 over pos_max if end of string
                   y_pos += y_comp.length
   
                   # we are comparing numbers - simply convert them
                   if x_seg_is_num
                     x_comp = x_comp.to_i
                     y_comp = y_comp.to_i
                   end
   
                   # compares ints or strings
                   # don't return if equal - try the next segment
                   if x_comp > y_comp
                     return 1
                   elsif x_comp < y_comp
                     return -1
                   end
   
                   # if we've reached here than the segments are the same - try again
                 end
   
                 # we must have reached the end of one or both of the strings and they
                 # matched up until this point
   
                 # segments matched completely but the segment separators were different -
                 # rpm reference code treats these as equal.
                 if (x_pos == x_pos_max + 1) and (y_pos == y_pos_max + 1)
                   return 0
                 end
   
                 # the most unprocessed characters left wins
                 if (x_pos_max - x_pos) > (y_pos_max - y_pos)
                   return 1
                 else
                   return -1
                 end
               end
   
             end # self
           end # RPMUtils
   
           class RPMVersion
             include Comparable
   
             def initialize(*args)
               if args.size == 1
                 @e, @v, @r = RPMUtils.version_parse(args[0])
               elsif args.size == 3
                 @e = args[0].to_i
                 @v = args[1]
                 @r = args[2]
               else
                 raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " +
                                      "version, release'"
               end
             end
             attr_reader :e, :v, :r
             alias :epoch :e
             alias :version :v
             alias :release :r
   
             def self.parse(*args)
               self.new(*args)
             end
   
             def <=>(y)
               compare_versions(y)
             end
   
             def compare(y)
               compare_versions(y, false)
             end
   
             def partial_compare(y)
               compare_versions(y, true)
             end
   
             # RPM::Version rpm_version_to_s equivalent
             def to_s
               if @r.nil?
                 @v
               else
                 "#{@v}-#{@r}"
               end
             end
   
             def evr
               "#{@e}:#{@v}-#{@r}"
             end
   
             private
   
             # Rough RPM::Version rpm_version_cmp equivalent - except much slower :)
             #
             # partial lets epoch and version segment equality be good enough to return equal, eg:
             #
             # 2:1.2-1 == 2:1.2
             # 2:1.2-1 == 2:
             #
             def compare_versions(y, partial=false)
               x = self
   
               # compare epoch
               if (x.e.nil? == false and x.e > 0) and y.e.nil?
                 return 1
               elsif x.e.nil? and (y.e.nil? == false and y.e > 0)
                 return -1
               elsif x.e.nil? == false and y.e.nil? == false
                 if x.e < y.e
                   return -1
                 elsif x.e > y.e
                   return 1
                 end
               end
   
               # compare version
               if partial and (x.v.nil? or y.v.nil?)
                 return 0
               elsif x.v.nil? == false and y.v.nil?
                 return 1
               elsif x.v.nil? and y.v.nil? == false
                 return -1
               elsif x.v.nil? == false and y.v.nil? == false
                 cmp = RPMUtils.rpmvercmp(x.v, y.v)
                 return cmp if cmp != 0
               end
   
               # compare release
               if partial and (x.r.nil? or y.r.nil?)
                 return 0
               elsif x.r.nil? == false and y.r.nil?
                 return 1
               elsif x.r.nil? and y.r.nil? == false
                 return -1
               elsif x.r.nil? == false and y.r.nil? == false
                 cmp = RPMUtils.rpmvercmp(x.r, y.r)
                 return cmp
               end
   
               return 0
             end
           end
   
           class RPMPackage
             include Comparable
   
             def initialize(*args)
               if args.size == 4
                 @n = args[0]
                 @version = RPMVersion.new(args[1])
                 @a = args[2]
                 @provides = args[3]
               elsif args.size == 6
                 @n = args[0]
                 e = args[1].to_i
                 v = args[2]
                 r = args[3]
                 @version = RPMVersion.new(e,v,r)
                 @a = args[4]
                 @provides = args[5]
               else
                 raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " +
                                      "or 'name, epoch, version, release, arch, provides'"
               end
   
               # We always have one, ourselves!
               if @provides.empty?
                 @provides = [ RPMProvide.new(@n, @version.evr, :==) ]
               end
             end
             attr_reader :n, :a, :version, :provides
             alias :name :n
             alias :arch :a
   
             def <=>(y)
               compare(y)
             end
   
             def compare(y)
               x = self
   
               # easy! :)
               return 0 if x.nevra == y.nevra
   
               # compare name
               if x.n.nil? == false and y.n.nil?
                 return 1
               elsif x.n.nil? and y.n.nil? == false
                 return -1
               elsif x.n.nil? == false and y.n.nil? == false
                 if x.n < y.n
                   return -1
                 elsif x.n > y.n
                   return 1
                 end
               end
   
               # compare version
               if x.version > y.version
                 return 1
               elsif x.version < y.version
                 return -1
               end
   
               # compare arch
               if x.a.nil? == false and y.a.nil?
                 return 1
               elsif x.a.nil? and y.a.nil? == false
                 return -1
               elsif x.a.nil? == false and y.a.nil? == false
                 if x.a < y.a
                   return -1
                 elsif x.a > y.a
                   return 1
                 end
               end
   
               return 0
             end
   
             def to_s
               nevra
             end
   
             def nevra
               "#{@n}-#{@version.evr}.#{@a}"
             end
           end
   
           # Simple implementation from rpm and ruby-rpm reference code
           class RPMDependency
             def initialize(*args)
               if args.size == 3
                 @name = args[0]
                 @version = RPMVersion.new(args[1])
                 # Our requirement to other dependencies
                 @flag = args[2] || :==
               elsif args.size == 5
                 @name = args[0]
                 e = args[1].to_i
                 v = args[2]
                 r = args[3]
                 @version = RPMVersion.new(e,v,r)
                 @flag = args[4] || :==
               else
                 raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " +
                                      "'name, epoch, version, release, flag'"
               end
             end
             attr_reader :name, :version, :flag
   
             # Parses 2 forms:
             #
             # "mtr >= 2:0.71-3.0"
             # "mta"
             def self.parse(string)
               if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}
                 name = $1
                 if $2 == "="
                   flag = :==
                 else
                   flag = :"#{$2}"
                 end
                 version = $3
   
                 return self.new(name, version, flag)
               else
                 name = string
                 return self.new(name, nil, nil)
               end
             end
   
             # Test if another RPMDependency satisfies our requirements
             def satisfy?(y)
               unless y.kind_of?(RPMDependency)
                 raise ArgumentError, "Expecting an RPMDependency object"
               end
   
               x = self
   
               # Easy!
               if x.name != y.name
                 return false
               end
   
               # Partial compare
               #
               # eg: x.version 2.3 == y.version 2.3-1
               sense = x.version.partial_compare(y.version)
   
               # Thanks to rpmdsCompare() rpmds.c
               if sense < 0 and (x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<)
                 return true
               elsif sense > 0 and (x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>)
                 return true
               elsif sense == 0 and (
                 ((x.flag == :== or x.flag == :<= or x.flag == :>=) and (y.flag == :== or y.flag == :<= or y.flag == :>=)) or
                 (x.flag == :< and y.flag == :<) or
                 (x.flag == :> and y.flag == :>)
               )
                 return true
               end
   
               return false
             end
           end
   
           class RPMProvide < RPMDependency; end
           class RPMRequire < RPMDependency; end
   
           class RPMDbPackage < RPMPackage
             # , installed, available
             def initialize(*args)
               # state
               @available = args.pop
               @installed = args.pop
               super(*args)
             end
             attr_reader :available, :installed
           end
   
           # Simple storage for RPMPackage objects - keeps them unique and sorted
           class RPMDb
             def initialize
               # package name => [ RPMPackage, RPMPackage ] of different versions
               @rpms = Hash.new
               # package nevra => RPMPackage for lookups
               @index = Hash.new
               # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
               @provides = Hash.new
               # RPMPackages listed as available
               @available = Set.new
               # RPMPackages listed as installed
               @installed = Set.new
             end
   
             def [](package_name)
               self.lookup(package_name)
             end
   
             # Lookup package_name and return a descending array of package objects
             def lookup(package_name)
               pkgs = @rpms[package_name]
               if pkgs
                 return pkgs.sort.reverse
               else
                 return nil
               end
             end
   
             def lookup_provides(provide_name)
               @provides[provide_name]
             end
   
             # Using the package name as a key, and nevra for an index, keep a unique list of packages.
             # The available/installed state can be overwritten for existing packages.
             def push(*args)
               args.flatten.each do |new_rpm|
                 unless new_rpm.kind_of?(RPMDbPackage)
                   raise ArgumentError, "Expecting an RPMDbPackage object"
                 end
   
                 @rpms[new_rpm.n] ||= Array.new
   
                 # we may already have this one, like when the installed list is refreshed
                 idx = @index[new_rpm.nevra]
                 if idx
                   # grab the existing package if it's not
                   curr_rpm = idx
                 else
                   @rpms[new_rpm.n] << new_rpm
   
                   new_rpm.provides.each do |provide|
                     @provides[provide.name] ||= Array.new
                     @provides[provide.name] << new_rpm
                   end
   
                   curr_rpm = new_rpm
                 end
   
                 # Track the nevra -> RPMPackage association to avoid having to compare versions
                 # with @rpms[new_rpm.n] on the next round
                 @index[new_rpm.nevra] = curr_rpm
   
                 # these are overwritten for existing packages
                 if new_rpm.available
                   @available << curr_rpm
                 end
                 if new_rpm.installed
                   @installed << curr_rpm
                 end
               end
             end
   
             def <<(*args)
               self.push(args)
             end
   
             def clear
               @rpms.clear
               @index.clear
               @provides.clear
               clear_available
               clear_installed
             end
   
             def clear_available
               @available.clear
             end
   
             def clear_installed
               @installed.clear
             end
   
             def size
               @rpms.size
             end
             alias :length :size
   
             def available_size
               @available.size
             end
   
             def installed_size
               @installed.size
             end
   
             def available?(package)
               @available.include?(package)
             end
   
             def installed?(package)
               @installed.include?(package)
             end
   
             def whatprovides(rpmdep)
               unless rpmdep.kind_of?(RPMDependency)
                 raise ArgumentError, "Expecting an RPMDependency object"
               end
   
               what = []
   
               packages = lookup_provides(rpmdep.name)
               if packages
                 packages.each do |pkg|
                   pkg.provides.each do |provide|
                     if provide.satisfy?(rpmdep)
                       what << pkg
                     end
                   end
                 end
               end
   
               return what
             end
           end
   
           # Cache for our installed and available packages, pulled in from yum-dump.py
           class YumCache
             include Chef::Mixin::Command
             include Singleton
   
             def initialize
               @rpmdb = RPMDb.new
   
               # Next time @rpmdb is accessed:
               #  :all       - Trigger a run of "yum-dump.py --options --installed-provides", updates
               #               yum's cache and parses options from /etc/yum.conf. Pulls in Provides
               #               dependency data for installed packages only - this data is slow to
               #               gather.
               #  :provides  - Same as :all but pulls in Provides data for available packages as well.
               #               Used as a last resort when we can't find a Provides match.
               #  :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm
               #               db. Used between client runs for a quick refresh.
               #  :none      - Do nothing, a call to one of the reload methods is required.
               @next_refresh = :all
   
               @allow_multi_install = []
   
               # these are for subsequent runs if we are on an interval
               Chef::Client.when_run_starts do
                 YumCache.instance.reload
               end
             end
   
             # Cache management
             #
   
             def refresh
               case @next_refresh
               when :none
                 return nil
               when :installed
                 reset_installed
                 # fast
                 opts=" --installed"
               when :all
                 reset
                 # medium
                 opts=" --options --installed-provides"
               when :provides
                 reset
                 # slow!
                 opts=" --options --all-provides"
               else
                 raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}"
               end
   
               one_line = false
               error = nil
   
               helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py')
   
               status = popen4("/usr/bin/python #{helper}#{opts}", :waitlast => true) do |pid, stdin, stdout, stderr|
                 stdout.each do |line|
                   one_line = true
   
                   line.chomp!
   
                   if line =~ %r{\[option (.*)\] (.*)}
                     if $1 == "installonlypkgs"
                       @allow_multi_install = $2.split
                     else
                       raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py"
                     end
                     next
                   end
   
                   if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r])$}
                     name     = $1
                     epoch    = $2
                     version  = $3
                     release  = $4
                     arch     = $5
                     provides = parse_provides($6)
                     type     = $7
                   else
                     Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " +
                                    "Please check your yum configuration.")
                     next
                   end
   
                   case type
                   when "i"
                     # if yum-dump was called with --installed this may not be true, but it's okay
                     # since we don't touch the @available Set in reload_installed
                     available = false
                     installed = true
                   when "a"
                     available = true
                     installed = false
                   when "r"
                     available = true
                     installed = true
                   end
   
                   pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available)
                   @rpmdb << pkg
                 end
   
                 error = stderr.readlines
               end
   
               if status.exitstatus != 0
                 raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}"
               else
                 unless one_line
                   Chef::Log.warn("Odd, no output from yum-dump.py. Please check " +
                                  "your yum configuration.")
                 end
               end
   
               # A reload method must be called before the cache is altered
               @next_refresh = :none
             end
   
             def reload
               @next_refresh = :all
             end
   
             def reload_installed
               @next_refresh = :installed
             end
   
             def reload_provides
               @next_refresh = :provides
             end
   
             def reset
               @rpmdb.clear
             end
   
             def reset_installed
               @rpmdb.clear_installed
             end
   
             # Querying the cache
             #
   
             # Check for package by name or name+arch
             def package_available?(package_name)
               refresh
   
               if @rpmdb.lookup(package_name)
                 return true
               else
                 if package_name =~ %r{^(.*)\.(.*)$}
                   pkg_name = $1
                   pkg_arch = $2
   
                   if matches = @rpmdb.lookup(pkg_name)
                     matches.each do |m|
                       return true if m.arch == pkg_arch
                     end
                   end
                 end
               end
   
               return false
             end
   
             # Returns a array of packages satisfying an RPMDependency
             def packages_from_require(rpmdep)
               refresh
               @rpmdb.whatprovides(rpmdep)
             end
   
             def version_available?(package_name, desired_version, arch=nil)
                version(package_name, arch, true, false) do |v|
                  return true if desired_version == v
                end
   
               return false
             end
   
             def available_version(package_name, arch=nil)
               version(package_name, arch, true, false)
             end
             alias :candidate_version :available_version
   
             def installed_version(package_name, arch=nil)
               version(package_name, arch, false, true)
             end
   
             def allow_multi_install
               refresh
               @allow_multi_install
             end
   
             private
   
             def version(package_name, arch=nil, is_available=false, is_installed=false)
               refresh
               packages = @rpmdb[package_name]
               if packages
                 packages.each do |pkg|
                   if is_available
                     next unless @rpmdb.available?(pkg)
                   end
                   if is_installed
                     next unless @rpmdb.installed?(pkg)
                   end
                   if arch
                     next unless pkg.arch == arch
                   end
   
                   if block_given?
                     yield pkg.version.to_s
                   else
                     # first match is latest version
                     return pkg.version.to_s
                   end
                 end
               end
   
               if block_given?
                 return self
               else
                 return nil
               end
             end
   
             # Parse provides from yum-dump.py output
             def parse_provides(string)
               ret = []
               # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0']
               string.split(", ").each do |seg|
                 # 'atk = 1.12.2-1.fc6'
                 if seg =~ %r{^'(.*)'$}
                   ret << RPMProvide.parse($1)
                 end
               end
   
               return ret
             end
   
           end # YumCache
   
           include Chef::Mixin::GetSourceFromPackage
   
           def initialize(new_resource, run_context)
             super
   
             @yum = YumCache.instance
           end
   
           # Extra attributes
           #
   
           def arch
             if @new_resource.respond_to?("arch")
               @new_resource.arch
             else
               nil
             end
           end
   
           def flush_cache
             if @new_resource.respond_to?("flush_cache")
               @new_resource.flush_cache
             else
               { :before => false, :after => false }
             end
           end
   
           def allow_downgrade
             if @new_resource.respond_to?("allow_downgrade")
               @new_resource.allow_downgrade
             else
               false
             end
           end
   
           # Helpers
           #
   
           def yum_arch
             arch ? ".#{arch}" : nil
           end
   
           def yum_command(command)
             status, stdout, stderr = output_of_command(command, {})
   
             # This is fun: rpm can encounter errors in the %post/%postun scripts which aren't
             # considered fatal - meaning the rpm is still successfully installed. These issue
             # cause yum to emit a non fatal warning but still exit(1). As there's currently no
             # way to suppress this behavior and an exit(1) will break a Chef run we make an
             # effort to trap these and re-run the same install command - it will either fail a
             # second time or succeed.
             #
             # A cleaner solution would have to be done in python and better hook into
             # yum/rpm to handle exceptions as we see fit.
             if status.exitstatus == 1
               stdout.each_line do |l|
                 # rpm-4.4.2.3 lib/psm.c line 2182
                 if l =~ %r{^error: %(post|postun)\(.*\) scriptlet failed, exit status \d+$}
                   Chef::Log.warn("#{@new_resource} caught non-fatal scriptlet issue: \"#{l}\". Can't trust yum exit status " +
                                  "so running install again to verify.")
                   status, stdout, stderr = output_of_command(command, {})
                   break
                 end
               end
             end
   
             if status.exitstatus > 0
               command_output = "STDOUT: #{stdout}"
               command_output << "STDERR: #{stderr}"
               handle_command_failures(status, command_output, {})
             end
           end
   
           # Standard Provider methods for Parent
           #
   
           def load_current_resource
             if flush_cache[:before]
               @yum.reload
             end
   
             # At this point package_name could be:
             # 
             # 1) a package name, eg: "foo"
             # 2) a package name.arch, eg: "foo.i386"
             # 3) or a dependency, eg: "foo >= 1.1"
   
             # Check if we have name or name+arch which has a priority over a dependency
             unless @yum.package_available?(@new_resource.package_name)
               # If they aren't in the installed packages they could be a dependency
               parse_dependency
             end
   
             # Don't overwrite an existing arch
             unless arch
               parse_arch
             end
   
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
   
             if @new_resource.source
               unless ::File.exists?(@new_resource.source)
                 raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
               end
   
               Chef::Log.debug("#{@new_resource} checking rpm status")
               status = popen4("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}") do |pid, stdin, stdout, stderr|
                 stdout.each do |line|
                   case line
                   when /([\w\d_.-]+)\s([\w\d_.-]+)/
                     @current_resource.package_name($1)
                     @new_resource.version($2)
                   end
                 end
               end
             end
   
             if @new_resource.version
               new_resource = "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch}"
             else
               new_resource = "#{@new_resource.package_name}#{yum_arch}"
             end
   
             Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
   
             installed_version = @yum.installed_version(@new_resource.package_name, arch)
             @current_resource.version(installed_version)
   
             @candidate_version = @yum.candidate_version(@new_resource.package_name, arch)
   
             Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " +
                             "#{@candidate_version || "(none)"}")
   
             @current_resource
           end
   
           def install_package(name, version)
             if @new_resource.source
               yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}")
             else
               # Work around yum not exiting with an error if a package doesn't exist for CHEF-2062
               if @yum.version_available?(name, version, arch)
                 method = "install"
   
                 # More Yum fun:
                 #
                 # yum install of an old name+version will exit(1)
                 # yum install of an old name+version+arch will exit(0) for some reason
                 #
                 # Some packages can be installed multiple times like the kernel
                 unless @yum.allow_multi_install.include?(name)
                   if RPMVersion.parse(@current_resource.version) > RPMVersion.parse(version)
                     # Unless they want this...
                     if allow_downgrade
                       method = "downgrade"
                     else
                       # we bail like yum when the package is older
                       raise Chef::Exceptions::Package, "Installed package #{name}-#{@current_resource.version} is newer " +
                                                        "than candidate package #{name}-#{version}"
                     end
                   end
                 end
   
                 yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{name}-#{version}#{yum_arch}")
               else
                 raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " +
                                                  "and release? (version-release, e.g. 1.84-10.fc6)"
               end
             end
             if flush_cache[:after]
               @yum.reload
             else
               @yum.reload_installed
             end
           end
   
           # Keep upgrades from trying to install an older candidate version. Can happen when a new
           # version is installed then removed from a repository, now the older available version
           # shows up as a viable install candidate.
           #
           # Can be done in upgrade_package but an upgraded from->to log message slips out
           #
           # Hacky - better overall solution? Custom compare in Package provider?
           def action_upgrade
             # Ensure the candidate is newer
             if RPMVersion.parse(candidate_version) > RPMVersion.parse(@current_resource.version)
               super
             # Candidate is older
             else
               Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do")
             end
           end
   
           def upgrade_package(name, version)
             install_package(name, version)
           end
   
           def remove_package(name, version)
             if version
               yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}-#{version}#{yum_arch}")
             else
               yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}#{yum_arch}")
             end
             if flush_cache[:after]
               @yum.reload
             else
               @yum.reload_installed
             end
           end
   
           def purge_package(name, version)
             remove_package(name, version)
           end
   
           private
   
           def parse_arch
             # Allow for foo.x86_64 style package_name like yum uses in it's output
             #
             if @new_resource.package_name =~ %r{^(.*)\.(.*)$}
               new_package_name = $1
               new_arch = $2
               # foo.i386 and foo.beta1 are both valid package names or expressions of an arch.
               # Ensure we don't have an existing package matching package_name, then ensure we at
               # least have a match for the new_package+new_arch before we overwrite. If neither
               # then fall through to standard package handling.
               if (@yum.installed_version(@new_resource.package_name).nil? and @yum.candidate_version(@new_resource.package_name).nil?) and
                    (@yum.installed_version(new_package_name, new_arch) or @yum.candidate_version(new_package_name, new_arch))
                  @new_resource.package_name(new_package_name)
                  @new_resource.arch(new_arch)
               end
             end
           end
   
           # If we don't have the package we could have been passed a 'whatprovides' feature
           #
           # eg: yum install "perl(Config)"
           #     yum install "mtr = 2:0.71-3.1"
           #     yum install "mtr > 2:0.71"
           #
           # We support resolving these out of the Provides data imported from yum-dump.py and
           # matching them up with an actual package so the standard resource handling can apply.
           #
           # There is currently no support for filename matching.
           def parse_dependency
             # Transform the package_name into a requirement
             yum_require = RPMRequire.parse(@new_resource.package_name)
             # and gather all the packages that have a Provides feature satisfying the requirement.
             # It could be multiple be we can only manage one
             packages = @yum.packages_from_require(yum_require)
   
             if packages.empty?
               # Don't bother if we are just ensuring a package is removed - we don't need Provides data
               actions = Array(@new_resource.action)
               unless actions.size == 1 and (actions[0] == :remove || actions[0] == :purge)
                 Chef::Log.debug("#{@new_resource} couldn't match #{@new_resource.package_name} in " +
                               "installed Provides, loading available Provides - this may take a moment")
                 @yum.reload_provides
                 packages = @yum.packages_from_require(yum_require)
               end
             end
   
             unless packages.empty?
               new_package_name = packages.first.name
               Chef::Log.debug("#{@new_resource} no package found for #{@new_resource.package_name} " +
                               "but matched Provides for #{new_package_name}")
   
               # Ensure it's not the same package under a different architecture
               unique_names = []
               packages.each do |pkg|
                 unique_names << "#{pkg.name}-#{pkg.version.evr}"
               end
               unique_names.uniq!
   
               if unique_names.size > 1
                 Chef::Log.warn("#{@new_resource} matched multiple Provides for #{@new_resource.package_name} " +
                                "but we can only use the first match: #{new_package_name}. Please use a more " +
                                "specific version.")
               end
   
               @new_resource.package_name(new_package_name)
             end
           end
   
         end
       end
     end
   end

lib/chef/certificate.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/config'
   require 'chef/api_client'
   require 'openssl'
   require 'fileutils'
   
   class Chef
     class Certificate
       class << self
     
         # Generates a new CA Certificate and Key, and writes them out to
         # Chef::Config[:signing_ca_cert] and Chef::Config[:signing_ca_key].
         def generate_signing_ca
           ca_cert_file = Chef::Config[:signing_ca_cert]
           ca_keypair_file = Chef::Config[:signing_ca_key] 
   
           unless File.exists?(ca_cert_file) && File.exists?(ca_keypair_file)
             Chef::Log.info("Creating new signing certificate")
           
             [ ca_cert_file, ca_keypair_file ].each do |f|
               ca_basedir = File.dirname(f)
               FileUtils.mkdir_p ca_basedir
             end
   
             keypair = OpenSSL::PKey::RSA.generate(1024)
   
             ca_cert = OpenSSL::X509::Certificate.new
             ca_cert.version = 3
             ca_cert.serial = 1
             info = [
               ["C", Chef::Config[:signing_ca_country]], 
               ["ST", Chef::Config[:signing_ca_state]], 
               ["L", Chef::Config[:signing_ca_location]], 
               ["O", Chef::Config[:signing_ca_org]],
               ["OU", "Certificate Service"], 
               ["CN", "#{Chef::Config[:signing_ca_domain]}/emailAddress=#{Chef::Config[:signing_ca_email]}"]
             ]
             ca_cert.subject = ca_cert.issuer = OpenSSL::X509::Name.new(info)
             ca_cert.not_before = Time.now
             ca_cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years
             ca_cert.public_key = keypair.public_key
   
             ef = OpenSSL::X509::ExtensionFactory.new
             ef.subject_certificate = ca_cert
             ef.issuer_certificate = ca_cert
             ca_cert.extensions = [
                     ef.create_extension("basicConstraints", "CA:TRUE", true),
                     ef.create_extension("subjectKeyIdentifier", "hash"),
                     ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
             ]
             ca_cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
             ca_cert.sign keypair, OpenSSL::Digest::SHA1.new
   
             File.open(ca_cert_file, "w") { |f| f.write ca_cert.to_pem }
             File.open(ca_keypair_file, File::WRONLY|File::EXCL|File::CREAT, 0600) { |f| f.write keypair.to_pem }
             if (Chef::Config[:signing_ca_user] && Chef::Config[:signing_ca_group])
               FileUtils.chown(Chef::Config[:signing_ca_user], Chef::Config[:signing_ca_group], ca_keypair_file)
             end
           end
           self
         end
   
         # Creates a new key pair, and signs them with the signing certificate
         # and key generated from generate_signing_ca above.  
         #
         # @param [String] The common name for the key pair.
         # @param [Optional String] The subject alternative name.
         # @return [Object, Object] The public and private key objects.
         def gen_keypair(common_name, subject_alternative_name = nil)
   
           Chef::Log.info("Creating new key pair for #{common_name}")
   
           # generate client keypair
           client_keypair = OpenSSL::PKey::RSA.generate(2048)
   
           client_cert = OpenSSL::X509::Certificate.new
   
           ca_cert = OpenSSL::X509::Certificate.new(File.read(Chef::Config[:signing_ca_cert]))
   
           info = [
             ["C", Chef::Config[:signing_ca_country]], 
             ["ST", Chef::Config[:signing_ca_state]], 
             ["L", Chef::Config[:signing_ca_location]], 
             ["O", Chef::Config[:signing_ca_org]],
             ["OU", "Certificate Service"], 
             ["CN", common_name ]
           ]
   
           client_cert.subject = OpenSSL::X509::Name.new(info)
           client_cert.issuer = ca_cert.subject
           client_cert.not_before = Time.now
           client_cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years
           client_cert.public_key = client_keypair.public_key
           client_cert.serial = 1
           client_cert.version = 3
   
           ef = OpenSSL::X509::ExtensionFactory.new
           ef.subject_certificate = client_cert
           ef.issuer_certificate = ca_cert
   
           client_cert.extensions = [
                   ef.create_extension("basicConstraints", "CA:FALSE", true),
                   ef.create_extension("subjectKeyIdentifier", "hash")
           ]
           client_cert.add_extension ef.create_extension("subjectAltName", subject_alternative_name) if subject_alternative_name
   
           client_cert.sign(OpenSSL::PKey::RSA.new(File.read(Chef::Config[:signing_ca_key])), OpenSSL::Digest::SHA1.new)
   
           return client_cert.public_key, client_keypair
         end
   
         def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key], admin=false)
           # Create the validation key
           api_client = Chef::ApiClient.new
           api_client.name(name)
           api_client.admin(admin)
           
           begin
             # If both the couch record and file exist, don't do anything. Otherwise,
             # re-generate the validation key.
             Chef::ApiClient.cdb_load(name)
             
             # The couch document was loaded successfully if we got to here; if we
             # can't also load the file on the filesystem, we'll regenerate it all.
             File.open(key_file, "r") do |file|
             end
           rescue Chef::Exceptions::CouchDBNotFound
             create_validation_key(api_client, key_file)
           rescue
             if $!.class.name =~ /Errno::/
               Chef::Log.error("Error opening validation key: #{$!} -- destroying and regenerating")
               begin
                 api_client.cdb_destroy
               rescue Bunny::ServerDownError => e
                 # create_validation_key is gonna fail anyway, so let's just bail out.
                 Chef::Log.fatal("Could not de-index (to rabbitmq) previous validation key - rabbitmq is down! Start rabbitmq then restart chef-server to re-generate it")
                 raise
               end
               
               create_validation_key(api_client, key_file)
             else
               raise
             end
           end
         end
         
         private
         def create_validation_key(api_client, key_file)
           Chef::Log.info("Creating validation key...")
   
           api_client.create_keys
           begin
             api_client.cdb_save
           rescue Bunny::ServerDownError => e
             # If rabbitmq is down, the client will have been saved in CouchDB,
             # but not in the index.
             Chef::Log.fatal("Could not index (to rabbitmq) validation key - rabbitmq is down! Start rabbitmq then restart chef-server to re-generate it")
   
             # re-raise so the error bubbles out and nukes chef-server
             raise e
           end
           
           key_dir = File.dirname(key_file)
           FileUtils.mkdir_p(key_dir) unless File.directory?(key_dir)
           File.open(key_file, File::WRONLY|File::CREAT, 0600) do |f|
             f.print(api_client.private_key)
           end
           if (Chef::Config[:signing_ca_user] && Chef::Config[:signing_ca_group])
             FileUtils.chown(Chef::Config[:signing_ca_user], Chef::Config[:signing_ca_group], key_file)
           end
         end
   
       end
     end
   end

lib/chef/provider/cron/solaris.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Author:: Toomas Pelberg (toomasp@gmx.net)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # Copyright:: Copyright (c) 2010 Toomas Pelberg
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Cron
         class Solaris < Chef::Provider::Cron
         include Chef::Mixin::Command
   
         CRON_PATTERN = /([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s(.*)/
   
         def initialize(new_resource, run_context)
           super(new_resource, run_context)
           @cron_exists = false
           @cron_empty = false
         end
         attr_accessor :cron_exists, :cron_empty
   
         def load_current_resource
           crontab_lines = []
           @current_resource = Chef::Resource::Cron.new(@new_resource.name)
           @current_resource.user(@new_resource.user)
           status = popen4("crontab -l #{@new_resource.user}") do |pid, stdin, stdout, stderr|
             stdout.each_line { |line| crontab_lines << line }
           end
           if status.exitstatus > 1
             raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}"
           elsif status.exitstatus == 0
             cron_found = false
             crontab_lines.each do |line|
               case line.chomp
               when "# Chef Name: #{@new_resource.name}"
                 Chef::Log.debug("#{@new_resource} found cron '#{@new_resource.name}'")
                 cron_found = true
                 @cron_exists = true
                 next
               when /^MAILTO=(\S*)/
                 @current_resource.mailto($1) if cron_found
                 next
               when /^PATH=(\S*)/
                 @current_resource.path($1) if cron_found
                 next
               when /^SHELL=(\S*)/
                 @current_resource.shell($1) if cron_found
                 next
               when /^HOME=(\S*)/
                 @current_resource.home($1) if cron_found
                 next
               when CRON_PATTERN
                 if cron_found
                   @current_resource.minute($1)
                   @current_resource.hour($2)
                   @current_resource.day($3)
                   @current_resource.month($4)
                   @current_resource.weekday($5)
                   @current_resource.command($6)
                   cron_found=false
                 end
                 next
               else
                 next
               end
             end
             Chef::Log.debug("#{@new_resource} cron '#{@new_resource.name}' not found") unless @cron_exists
           elsif status.exitstatus == 1
             Chef::Log.debug("#{@new_resource} cron empty for '#{@new_resource.user}'")
             @cron_empty = true
           end
   
           @current_resource
         end
   
         def compare_cron
           [ :minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home ].any? do |cron_var|
             !@new_resource.send(cron_var).nil? && @new_resource.send(cron_var) != @current_resource.send(cron_var)
           end
         end
   
         def write_crontab(crontab)
           tempcron = Tempfile.new("chef-cron")
           tempcron << crontab
           tempcron.flush
           tempcron.chmod(0644)
           status = run_command(:command => "/usr/bin/crontab #{tempcron.path}",:user => @new_resource.user)
           if(status == 0)
             @new_resource.updated_by_last_action(true)
           else
             @new_resource.updated_by_last_action(false)
           end
           tempcron.close!
           return status
         end
   
         def action_create
           crontab = String.new
           newcron = String.new
           cron_found = false
   
           newcron << "# Chef Name: #{new_resource.name}\n"
           [ :mailto, :path, :shell, :home ].each do |v|
             newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v)
           end
           newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
   
           if @cron_exists
             unless compare_cron
               Chef::Log.debug("#{@new_resource} skipping existing cron entry '#{@new_resource.name}'")
               return
             end
             status = popen4("crontab -l #{@new_resource.user}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line.chomp
                 when "# Chef Name: #{@new_resource.name}"
                   cron_found = true
                   next
                 when CRON_PATTERN
                   if cron_found
                     cron_found = false
                     crontab << newcron
                     next
                   end
                 else
                   next if cron_found
                 end
                 crontab << line
               end
             end
   
             status = write_crontab(crontab)
             Chef::Log.info("#{@new_resource} updated crontab entry")
           else
             unless @cron_empty
               status = popen4("crontab -l #{@new_resource.user}") do |pid, stdin, stdout, stderr|
                 stdout.each { |line| crontab << line }
               end
             end
   
             crontab << newcron
             status = write_crontab(crontab)
             Chef::Log.info("#{@new_resource} added crontab entry")
           end
         end
   
         def action_delete
           if @cron_exists
             crontab = String.new
             cron_found = false
             status = popen4("crontab -l #{@new_resource.user}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line.chomp
                 when "# Chef Name: #{@new_resource.name}"
                   cron_found = true
                   next
                 when CRON_PATTERN
                   if cron_found
                     cron_found = false
                     next
                   end
                 else
                   next if cron_found
                 end
                 crontab << line
               end
             end
   
             status = write_crontab(crontab)
             Chef::Log.info("#{@new_resource} deleted crontab entry")
           end
         end
   
       end
     end
   end
   end

lib/chef/knife/cookbook_create.rb

   #
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookCreate < Knife
   
         deps do
           require 'chef/json_compat'
           require 'uri'
           require 'fileutils'
         end
   
         banner "knife cookbook create COOKBOOK (options)"
   
         option :cookbook_path,
           :short => "-o PATH",
           :long => "--cookbook-path PATH",
           :description => "The directory where the cookbook will be created"
   
         option :readme_format,
           :short => "-r FORMAT",
           :long => "--readme-format FORMAT",
           :description => "Format of the README file, supported formats are 'md' (markdown) and 'rdoc' (rdoc)"
   
         option :cookbook_license,
           :short => "-I LICENSE",
           :long => "--license LICENSE",
           :description => "License for cookbook, apachev2, gplv2, gplv3, mit or none"
   
         option :cookbook_copyright,
           :short => "-C COPYRIGHT",
           :long => "--copyright COPYRIGHT",
           :description => "Name of Copyright holder"
   
         option :cookbook_email,
           :short => "-m EMAIL",
           :long => "--email EMAIL",
           :description => "Email address of cookbook maintainer"
   
         def run
           self.config = Chef::Config.merge!(config)
           if @name_args.length < 1
             show_usage
             ui.fatal("You must specify a cookbook name")
             exit 1
           end
   
           if default_cookbook_path_empty? && parameter_empty?(config[:cookbook_path])
             raise ArgumentError, "Default cookbook_path is not specified in the knife.rb config file, and a value to -o is not provided. Nowhere to write the new cookbook to."
           end
   
           cookbook_path = File.expand_path(Array(config[:cookbook_path]).first)
           cookbook_name = @name_args.first
           copyright = config[:cookbook_copyright] || "YOUR_COMPANY_NAME"
           email = config[:cookbook_email] || "YOUR_EMAIL"
           license = ((config[:cookbook_license] != "false") && config[:cookbook_license]) || "none"
           readme_format = ((config[:readme_format] != "false") && config[:readme_format]) || "md"
           create_cookbook(cookbook_path,cookbook_name, copyright, license)
           create_readme(cookbook_path,cookbook_name,readme_format)
           create_metadata(cookbook_path,cookbook_name, copyright, email, license,readme_format)
         end
   
         def create_cookbook(dir, cookbook_name, copyright, license)
           msg("** Creating cookbook #{cookbook_name}")
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "attributes")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "recipes")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "definitions")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "libraries")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "resources")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "providers")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "files", "default")}"
           FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "templates", "default")}"
           unless File.exists?(File.join(dir, cookbook_name, "recipes", "default.rb"))
             open(File.join(dir, cookbook_name, "recipes", "default.rb"), "w") do |file|
               file.puts <<-EOH
   #
   # Cookbook Name:: #{cookbook_name}
   # Recipe:: default
   #
   # Copyright #{Time.now.year}, #{copyright}
   #
   EOH
               case license
               when "apachev2"
                 file.puts <<-EOH
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   EOH
               when "gplv2"
                 file.puts <<-EOH
   # This program is free software; you can redistribute it and/or modify
   # it under the terms of the GNU General Public License as published by
   # the Free Software Foundation; either version 2 of the License, or
   # (at your option) any later version.
   #
   # This program is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License along
   # with this program; if not, write to the Free Software Foundation, Inc.,
   # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   #
   EOH
               when "gplv3"
                 file.puts <<-EOH
   # This program is free software: you can redistribute it and/or modify
   # it under the terms of the GNU General Public License as published by
   # the Free Software Foundation, either version 3 of the License, or
   # (at your option) any later version.
   #
   # This program is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License
   # along with this program.  If not, see .
   #
   EOH
               when "mit"
                 file.puts <<-EOH
   # Permission is hereby granted, free of charge, to any person obtaining
   # a copy of this software and associated documentation files (the
   # "Software"), to deal in the Software without restriction, including
   # without limitation the rights to use, copy, modify, merge, publish,
   # distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so, subject to
   # the following conditions:
   #
   # The above copyright notice and this permission notice shall be
   # included in all copies or substantial portions of the Software.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   EOH
               when "none"
                 file.puts <<-EOH
   # All rights reserved - Do Not Redistribute
   #
   EOH
               end
             end
           end
         end
   
         def create_readme(dir, cookbook_name,readme_format)
           msg("** Creating README for cookbook: #{cookbook_name}")
           unless File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}"))
             open(File.join(dir, cookbook_name, "README.#{readme_format}"), "w") do |file|
               case readme_format
               when "rdoc"
                 file.puts <<-EOH
   = DESCRIPTION:
   
   = REQUIREMENTS:
   
   = ATTRIBUTES:
   
   = USAGE:
   
   EOH
               when "md","mkd","txt"
                 file.puts <<-EOH
   Description
   ===========
   
   Requirements
   ============
   
   Attributes
   ==========
   
   Usage
   =====
   
   EOH
               else
                 file.puts <<-EOH
   Description
   
   Requirements
   
   Attributes
   
   Usage
   
   EOH
               end
             end
           end
         end
   
         def create_metadata(dir, cookbook_name, copyright, email, license,readme_format)
           msg("** Creating metadata for cookbook: #{cookbook_name}")
   
           license_name = case license
                          when "apachev2"
                            "Apache 2.0"
                          when "gplv2"
                            "GNU Public License 2.0"
                          when "gplv3"
                            "GNU Public License 3.0"
                          when "mit"
                            "MIT"
                          when "none"
                            "All rights reserved"
                          end
   
           unless File.exists?(File.join(dir, cookbook_name, "metadata.rb"))
             open(File.join(dir, cookbook_name, "metadata.rb"), "w") do |file|
               if File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}"))
                 long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.#{readme_format}'))"
               end
               file.puts <<-EOH
   maintainer       "#{copyright}"
   maintainer_email "#{email}"
   license          "#{license_name}"
   description      "Installs/Configures #{cookbook_name}"
   #{long_description}
   version          "0.0.1"
   EOH
             end
           end
         end
   
         private
   
         def default_cookbook_path_empty?
           Chef::Config[:cookbook_path].nil? || Chef::Config[:cookbook_path].empty?
         end
   
         def parameter_empty?(parameter)
           parameter.nil? || parameter.empty?
         end
   
       end
     end
   end

lib/chef/couchdb.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/mixin/params_validate'
   require 'chef/config'
   require 'chef/rest'
   require 'chef/log'
   require 'digest/sha2'
   require 'chef/json_compat'
   
   # We want to fail on create if uuidtools isn't installed
   begin
     require 'uuidtools'
   rescue LoadError
   end
   
   class Chef
     class CouchDB
       include Chef::Mixin::ParamsValidate
   
       def initialize(url=nil, db=Chef::Config[:couchdb_database])
         url ||= Chef::Config[:couchdb_url]
         @db = db
         @rest = Chef::REST.new(url, nil, nil)
       end
   
       def couchdb_database(args=nil)
         @db = args || @db
       end
   
       def create_id_map
         create_design_document(
           "id_map",
           {
             "version" => 1,
             "language" => "javascript",
             "views" => {
               "name_to_id" => {
                 "map" => <<-EOJS
                   function(doc) {
                     emit([ doc.chef_type, doc.name], doc._id);
                   }
                 EOJS
               },
               "id_to_name" => {
                 "map" => <<-EOJS
                   function(doc) {
                     emit(doc._id, [ doc.chef_type, doc.name ]);
                   }
                 EOJS
               }
             }
           }
         )
       end
   
       def create_db(check_for_existing=true)
         @database_list = @rest.get_rest("_all_dbs")
         if !check_for_existing || !@database_list.any? { |db| db == couchdb_database }
           response = @rest.put_rest(couchdb_database, Hash.new)
         end
         couchdb_database
       end
   
       def create_design_document(name, data)
         to_update = true
         begin
           old_doc = @rest.get_rest("#{couchdb_database}/_design/#{name}")
           if data["version"] != old_doc["version"]
             data["_rev"] = old_doc["_rev"]
             Chef::Log.debug("Updating #{name} views")
           else
             to_update = false
           end
         rescue
           Chef::Log.debug("Creating #{name} views for the first time because: #{$!}")
         end
         if to_update
           @rest.put_rest("#{couchdb_database}/_design%2F#{name}", data)
         end
         true
       end
   
       # Save the object to Couch. Add to index if the object supports it.
       def store(obj_type, name, object)
         validate(
           {
             :obj_type => obj_type,
             :name => name,
             :object => object,
           },
           {
             :object => { :respond_to => :to_json },
           }
         )
         rows = get_view("id_map", "name_to_id", :key => [ obj_type, name ])["rows"]
         uuid = rows.empty? ? UUIDTools::UUID.random_create.to_s : rows.first.fetch("id")
   
         db_put_response = @rest.put_rest("#{couchdb_database}/#{uuid}", object)
   
         if object.respond_to?(:add_to_index)
           Chef::Log.info("Sending #{obj_type}(#{uuid}) to the index queue for addition.")
           object.add_to_index(:database => couchdb_database, :id => uuid, :type => obj_type)
         end
   
         db_put_response
       end
   
       def load(obj_type, name)
         validate(
           {
             :obj_type => obj_type,
             :name => name,
           },
           {
             :obj_type => { :kind_of => String },
             :name => { :kind_of => String },
           }
                  )
         doc = find_by_name(obj_type, name)
         doc.couchdb = self if doc.respond_to?(:couchdb)
         doc
       end
   
       def delete(obj_type, name, rev=nil)
         validate(
           {
             :obj_type => obj_type,
             :name => name,
           },
           {
             :obj_type => { :kind_of => String },
             :name => { :kind_of => String },
           }
         )
         del_id = nil
         object, uuid = find_by_name(obj_type, name, true)
         unless rev
           if object.respond_to?(:couchdb_rev)
             rev = object.couchdb_rev
           else
             rev = object['_rev']
           end
         end
         response = @rest.delete_rest("#{couchdb_database}/#{uuid}?rev=#{rev}")
         response.couchdb = self if response.respond_to?(:couchdb=)
   
         if object.respond_to?(:delete_from_index)
           Chef::Log.info("Sending #{obj_type}(#{uuid}) to the index queue for deletion..")
           object.delete_from_index(:database => couchdb_database, :id => uuid, :type => obj_type)
         end
   
         response
       end
   
       def list(view, inflate=false)
         validate(
           {
             :view => view,
           },
           {
             :view => { :kind_of => String }
           }
         )
         if inflate
           r = @rest.get_rest(view_uri(view, "all"))
           r["rows"].each { |i| i["value"].couchdb = self if i["value"].respond_to?(:couchdb=) }
           r
         else
           r = @rest.get_rest(view_uri(view, "all_id"))
         end
         r
       end
   
       def has_key?(obj_type, name)
         validate(
           {
             :obj_type => obj_type,
             :name => name,
           },
           {
             :obj_type => { :kind_of => String },
             :name => { :kind_of => String },
           }
         )
         begin
           find_by_name(obj_type, name)
           true
         rescue
           false
         end
       end
   
       def find_by_name(obj_type, name, with_id=false)
         r = get_view("id_map", "name_to_id", :key => [ obj_type, name ], :include_docs => true)
         if r["rows"].length == 0
           raise Chef::Exceptions::CouchDBNotFound, "Cannot find #{obj_type} #{name} in CouchDB!"
         end
         if with_id
           [ r["rows"][0]["doc"], r["rows"][0]["id"] ]
         else
           r["rows"][0]["doc"]
         end
       end
   
       def get_view(design, view, options={})
         view_string = view_uri(design, view)
         view_string << "?" if options.length != 0
         view_string << options.map { |k,v| "#{k}=#{URI.escape(v.to_json)}"}.join('&')
         @rest.get_rest(view_string)
       end
   
       def bulk_get(*to_fetch)
         response = @rest.post_rest("#{couchdb_database}/_all_docs?include_docs=true", { "keys" => to_fetch.flatten })
         response["rows"].collect { |r| r["doc"] }
       end
   
       def view_uri(design, view)
         "#{couchdb_database}/_design/#{design}/_view/#{view}"
       end
   
       def server_stats
         @rest.get_rest('/')
       end
   
       def db_stats
         @rest.get_rest("/#{@db}")
       end
   
     end
   end

lib/chef/cookbook_site_streaming_uploader.rb

   #
   # Author:: Stanislav Vitvitskiy
   # Author:: Nuo Yan (nuo@opscode.com)
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'net/http'
   require 'mixlib/authentication/signedheaderauth'
   require 'openssl'
   
   class Chef
     # == Chef::CookbookSiteStreamingUploader
     # A streaming multipart HTTP upload implementation. Used to upload cookbooks
     # (in tarball form) to http://cookbooks.opscode.com
     #
     # inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
     class CookbookSiteStreamingUploader
   
       DefaultHeaders = { 'accept' => 'application/json', 'x-chef-version' => ::Chef::VERSION }
   
       class << self
   
          def create_build_dir(cookbook)
            tmp_cookbook_path = Tempfile.new("chef-#{cookbook.name}-build")
            tmp_cookbook_path.close
            tmp_cookbook_dir = tmp_cookbook_path.path
            File.unlink(tmp_cookbook_dir)
            FileUtils.mkdir_p(tmp_cookbook_dir)
            Chef::Log.debug("Staging at #{tmp_cookbook_dir}")
            checksums_to_on_disk_paths = cookbook.checksums
            Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
              cookbook.manifest[segment].each do |manifest_record|
                path_in_cookbook = manifest_record[:path]
                on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]]
                dest = File.join(tmp_cookbook_dir, cookbook.name.to_s, path_in_cookbook)
                FileUtils.mkdir_p(File.dirname(dest))
                Chef::Log.debug("Staging #{on_disk_path} to #{dest}")
                FileUtils.cp(on_disk_path, dest)
             end
           end
   
           # First, generate metadata
           Chef::Log.debug("Generating metadata")
           kcm = Chef::Knife::CookbookMetadata.new
           kcm.config[:cookbook_path] = [ tmp_cookbook_dir ]
           kcm.name_args = [ cookbook.name.to_s ]
           kcm.run
   
           tmp_cookbook_dir
         end
   
         def post(to_url, user_id, secret_key_filename, params = {}, headers = {})
           make_request(:post, to_url, user_id, secret_key_filename, params, headers)
         end
   
         def put(to_url, user_id, secret_key_filename, params = {}, headers = {})
           make_request(:put, to_url, user_id, secret_key_filename, params, headers)
         end
   
         def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {})
           boundary = '----RubyMultipartClient' + rand(1000000).to_s + 'ZZZZZ'
           parts = []
           content_file = nil
   
           timestamp = Time.now.utc.iso8601
           secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename))
   
           unless params.nil? || params.empty?
             params.each do |key, value|
               if value.kind_of?(File)
                 content_file = value
                 filepath = value.path
                 filename = File.basename(filepath)
                 parts << StringPart.new( "--" + boundary + "\r\n" +
                                          "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" +
                                          "Content-Type: application/octet-stream\r\n\r\n")
                 parts << StreamPart.new(value, File.size(filepath))
                 parts << StringPart.new("\r\n")
               else
                 parts << StringPart.new( "--" + boundary + "\r\n" +
                                          "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n")
                 parts << StringPart.new(value.to_s + "\r\n")
               end
             end
             parts << StringPart.new("--" + boundary + "--\r\n")
           end
   
           body_stream = MultipartStream.new(parts)
   
           timestamp = Time.now.utc.iso8601
   
           url = URI.parse(to_url)
   
           Chef::Log.logger.debug("Signing: method: #{http_verb}, path: #{url.path}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}")
   
           # We use the body for signing the request if the file parameter
           # wasn't a valid file or wasn't included. Extract the body (with
           # multi-part delimiters intact) to sign the request.
           # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and
           # always hash the entire request body. In the file case it would just be
           # expanded multipart text - the entire body of the POST.
           content_body = parts.inject("") { |result,part| result + part.read(0, part.size) }
           content_file.rewind if content_file # we consumed the file for the above operation, so rewind it.
   
           signing_options = {
             :http_method=>http_verb,
             :path=>url.path,
             :user_id=>user_id,
             :timestamp=>timestamp}
           (content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || ""))
   
           headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key))
   
           content_file.rewind if content_file
   
           # net/http doesn't like symbols for header keys, so we'll to_s each one just in case
           headers = DefaultHeaders.merge(Hash[*headers.map{ |k,v| [k.to_s, v] }.flatten])
   
           req = case http_verb
                 when :put
                   Net::HTTP::Put.new(url.path, headers)
                 when :post
                   Net::HTTP::Post.new(url.path, headers)
                 end
           req.content_length = body_stream.size
           req.content_type = 'multipart/form-data; boundary=' + boundary unless parts.empty?
           req.body_stream = body_stream
   
           http = Net::HTTP.new(url.host, url.port)
           if url.scheme == "https"
             http.use_ssl = true
             http.verify_mode = OpenSSL::SSL::VERIFY_NONE
           end
           res = http.request(req)
           #res = http.start {|http_proc| http_proc.request(req) }
   
           # alias status to code and to_s to body for test purposes
           # TODO: stop the following madness!
           class << res
             alias :to_s :body
   
             # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
             def headers
               self
             end
   
             def status
               code.to_i
             end
           end
           res
         end
   
       end
   
       class StreamPart
         def initialize(stream, size)
           @stream, @size = stream, size
         end
   
         def size
           @size
         end
   
         # read the specified amount from the stream
         def read(offset, how_much)
           @stream.read(how_much)
         end
       end
   
       class StringPart
         def initialize(str)
           @str = str
         end
   
         def size
           @str.length
         end
   
         # read the specified amount from the string startiung at the offset
         def read(offset, how_much)
           @str[offset, how_much]
         end
       end
   
       class MultipartStream
         def initialize(parts)
           @parts = parts
           @part_no = 0
           @part_offset = 0
         end
   
         def size
           @parts.inject(0) {|size, part| size + part.size}
         end
   
         def read(how_much)
           return nil if @part_no >= @parts.size
   
           how_much_current_part = @parts[@part_no].size - @part_offset
   
           how_much_current_part = if how_much_current_part > how_much
                                     how_much
                                   else
                                     how_much_current_part
                                   end
   
           how_much_next_part = how_much - how_much_current_part
   
           current_part = @parts[@part_no].read(@part_offset, how_much_current_part)
   
           # recurse into the next part if the current one was not large enough
           if how_much_next_part > 0
             @part_no += 1
             @part_offset = 0
             next_part = read(how_much_next_part)
             current_part + if next_part
                              next_part
                            else
                              ''
                            end
           else
             @part_offset += how_much_current_part
             current_part
           end
         end
       end
   
     end
   end

lib/chef/provider/mount/mount.rb

   #
   # Author:: Joshua Timberman ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/mount'
   require 'chef/log'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Provider
       class Mount
         class Mount < Chef::Provider::Mount
           include Chef::Mixin::ShellOut
   
           def initialize(new_resource, run_context)
             super
             @real_device = nil
           end
           attr_accessor :real_device
   
           def load_current_resource
             @current_resource = Chef::Resource::Mount.new(@new_resource.name)
             @current_resource.mount_point(@new_resource.mount_point)
             @current_resource.device(@new_resource.device)
             mounted?
             enabled?
           end
           
           def mountable?
             # only check for existence of non-remote devices
             if (device_should_exist? && !::File.exists?(device_real) )
               raise Chef::Exceptions::Mount, "Device #{@new_resource.device} does not exist"
             elsif( !::File.exists?(@new_resource.mount_point) )
               raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist"
             end
             return true
           end
           
           def enabled?
             # Check to see if there is a entry in /etc/fstab. Last entry for a volume wins.
             enabled = false
             ::File.foreach("/etc/fstab") do |line|
               case line
               when /^[#\s]/
                 next
               when /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
                 enabled = true
                 @current_resource.fstype($1)
                 @current_resource.options($2)
                 @current_resource.dump($3.to_i)
                 @current_resource.pass($4.to_i)
                 Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
                 next
               when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/
                 enabled = false
                 Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
               end
             end
             @current_resource.enabled(enabled)
           end
           
           def mounted?
             mounted = false
             shell_out!("mount").stdout.each_line do |line|
               case line
               when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(@new_resource.mount_point)}/
                 mounted = true
                 Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
               when /^([\/\w])+\son\s#{Regexp.escape(@new_resource.mount_point)}\s+/
                 mounted = false
                 Chef::Log.debug("Special device #{$~[1]} mounted as #{@new_resource.mount_point}")
               end
             end
             @current_resource.mounted(mounted)
           end
   
           def mount_fs
             unless @current_resource.mounted
               mountable?
               command = "mount -t #{@new_resource.fstype}"
               command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty?
               command << case @new_resource.device_type
               when :device
                 " #{device_real}"
               when :label
                 " -L #{@new_resource.device}"
               when :uuid
                 " -U #{@new_resource.device}"
               end
               command << " #{@new_resource.mount_point}"
               shell_out!(command)
               Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
             else
               Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
             end
           end
   
           def umount_fs
             if @current_resource.mounted
               shell_out!("umount #{@new_resource.mount_point}")
               Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
             else
               Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
             end
           end
   
           def remount_fs
             if @current_resource.mounted and @new_resource.supports[:remount]
               shell_out!("mount -o remount #{@new_resource.mount_point}")
               @new_resource.updated_by_last_action(true)
               Chef::Log.debug("#{@new_resource} is remounted at #{@new_resource.mount_point}")
             elsif @current_resource.mounted
               umount_fs
               sleep 1
               mount_fs
             else
               Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do")
             end
           end
   
           def enable_fs
             if @current_resource.enabled && mount_options_unchanged?
               Chef::Log.debug("#{@new_resource} is already enabled - nothing to do")
               return nil
             end
             
             if @current_resource.enabled
               # The current options don't match what we have, so
               # disable, then enable.
               disable_fs
             end
             ::File.open("/etc/fstab", "a") do |fstab|
               fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
               Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}")
             end
           end
   
           def disable_fs
             if @current_resource.enabled
               contents = []
               
               found = false
               ::File.readlines("/etc/fstab").reverse_each do |line|
                 if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}/
                   found = true
                   Chef::Log.debug("#{@new_resource} is removed from fstab")
                   next
                 else
                   contents << line
                 end
               end
               
               ::File.open("/etc/fstab", "w") do |fstab|
                 contents.reverse_each { |line| fstab.puts line}
               end
             else
               Chef::Log.debug("#{@new_resource} is not enabled - nothing to do")
             end
           end
   
           def device_should_exist?
             @new_resource.device !~ /:/ && @new_resource.device !~ /\/\// && @new_resource.device != "tmpfs" && @new_resource.fstype != 'fuse'
           end
   
           private
   
           def device_fstab
             case @new_resource.device_type
             when :device
               @new_resource.device
             when :label
               "LABEL=#{@new_resource.device}"
             when :uuid
               "UUID=#{@new_resource.device}"
             end
           end
   
           def device_real
             if @real_device == nil 
               if @new_resource.device_type == :device
                 @real_device = @new_resource.device
               else
                 @real_device = ""
                 status = popen4("/sbin/findfs #{device_fstab}") do |pid, stdin, stdout, stderr|
                   device_line = stdout.first # stdout.first consumes
                   @real_device = device_line.chomp unless device_line.nil?
                 end
               end
             end
             @real_device
           end
   
           def device_logstring
             case @new_resource.device_type
             when :device
               "#{device_real}"
             when :label
               "#{device_real} with label #{@new_resource.device}"
             when :uuid
               "#{device_real} with uuid #{@new_resource.device}"
             end
           end
   
           def device_mount_regex
             ::File.symlink?(device_real) ? "(?:#{Regexp.escape(device_real)})|(?:#{Regexp.escape(::File.readlink(device_real))})" : Regexp.escape(device_real)
           end
   
           def device_fstab_regex
             if @new_resource.device_type == :device
               device_mount_regex
             else
               device_fstab
             end
           end
           
           def mount_options_unchanged?
             @current_resource.fstype == @new_resource.fstype and
             @current_resource.options == @new_resource.options and
             @current_resource.dump == @new_resource.dump and
             @current_resource.pass == @new_resource.pass
           end
           
         end
       end
     end
   end

lib/chef/provider/service/freebsd.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Freebsd < Chef::Provider::Service::Init
   
           def load_current_resource
             @current_resource = Chef::Resource::Service.new(@new_resource.name)
             @current_resource.service_name(@new_resource.service_name)
   
             # Determine if we're talking about /etc/rc.d or /usr/local/etc/rc.d
             if ::File.exists?("/etc/rc.d/#{current_resource.service_name}")
               @init_command = "/etc/rc.d/#{current_resource.service_name}"
             elsif ::File.exists?("/usr/local/etc/rc.d/#{current_resource.service_name}")
               @init_command = "/usr/local/etc/rc.d/#{current_resource.service_name}"
             else
               raise Chef::Exceptions::Service, "#{@new_resource}: unable to locate the rc.d script"
             end
             Chef::Log.debug("#{@current_resource} found at #{@init_command}")
   
             if @new_resource.supports[:status]
               begin
                 if run_command(:command => "#{@init_command} status") == 0
                   @current_resource.running true
                   Chef::Log.debug("#{@new_resource} is running")
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
   
             elsif @new_resource.status_command
               begin
                 if run_command(:command => @new_resource.status_command) == 0
                   @current_resource.running true
                   Chef::Log.debug("#{@new_resource} is running")
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
   
             else
               Chef::Log.debug("#{@new_resource} does not support status and you have not specified a status command, falling back to process table inspection")
   
               if node[:command][:ps].nil? or node[:command][:ps].empty?
                 raise Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this nodes 'ps' attribute"
               end
   
               status = popen4(node[:command][:ps]) do |pid, stdin, stdout, stderr|
                 r = Regexp.new(@new_resource.pattern)
                 Chef::Log.debug("#{@new_resource} attempting to match #{@new_resource.pattern} (#{r}) against process table")
                 stdout.each_line do |line|
                   if r.match(line)
                     @current_resource.running true
                     break
                   end
                 end
                 @current_resource.running false unless @current_resource.running
               end
               unless status.exitstatus == 0
                 raise Chef::Exceptions::Service, "Command #{node[:command][:ps]} failed"
               else
                 Chef::Log.debug("#{@new_resource} #{node[:command][:ps]} exited and parsed successfully, process running: #{@current_resource.running}")
               end
             end
   
             if ::File.exists?("/etc/rc.conf")
               read_rc_conf.each do |line|
                 case line
                 when /#{Regexp.escape(service_enable_variable_name)}="(\w+)"/
                   if $1 =~ /[Yy][Ee][Ss]/
                     @current_resource.enabled true
                   elsif $1 =~ /[Nn][Oo][Nn]?[Oo]?[Nn]?[Ee]?/
                     @current_resource.enabled false
                   end
                 end
               end
             end
             unless @current_resource.enabled
               Chef::Log.debug("#{@new_resource.name} enable/disable state unknown")
             end
   
             @current_resource
           end
   
           def read_rc_conf
             ::File.open("/etc/rc.conf", 'r') { |file| file.readlines }
           end
   
           def write_rc_conf(lines)
             ::File.open("/etc/rc.conf", 'w') do |file|
               lines.each { |line| file.puts(line) }
             end
           end
   
   
           # The variable name used in /etc/rc.conf for enabling this service
           def service_enable_variable_name
             # Look for name="foo" in the shell script @init_command. Use this for determining the variable name in /etc/rc.conf
             # corresponding to this service
             # For example: to enable the service mysql-server with the init command /usr/local/etc/rc.d/mysql-server, you need
             # to set mysql_enable="YES" in /etc/rc.conf
             makefile = ::File.open(@init_command)
             makefile.each do |line|
               case line
               when /^name="?(\w+)"?/
                 return $1 + "_enable"
               end
             end
             raise Chef::Exceptions::Service, "Could not find name=\"service\" line in #{@init_command}"
           end
   
           def set_service_enable(value)
             lines = read_rc_conf
             # Remove line that set the old value
             lines.delete_if { |line| line =~ /#{service_enable_variable_name}/ }
             # And append the line that sets the new value at the end
             lines << "#{service_enable_variable_name}=\"#{value}\""
             write_rc_conf(lines)
           end
   
           def enable_service()
             set_service_enable("YES") unless @current_resource.enabled
           end
   
           def disable_service()
             set_service_enable("NO") if @current_resource.enabled
           end
   
         end
       end
     end
   end

lib/chef/provider/ifconfig.rb

   #
   # Author:: Jason K. Jackson (jasonjackson@gmail.com)
   # Copyright:: Copyright (c) 2009 Jason K. Jackson
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   require 'erb'
   
   #  Recipe example:
   #
   #    int = {Hash with your network settings...}
   #
   #    ifconfig  int['ip'] do 
   #      ignore_failure  true 
   #      device  int['dev'] 
   #      mask    int['mask']  
   #      gateway int['gateway'] 
   #      mtu     int['mtu']
   #    end
   
   class Chef
     class Provider
       class Ifconfig < Chef::Provider
         include Chef::Mixin::Command
   
         def load_current_resource
           @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name)
   
           @interfaces = {}
   
           status = popen4("ifconfig") do |pid, stdin, stdout, stderr|
             stdout.each do |line|
   
               if !line[0..9].strip.empty?
                 @int_name = line[0..9].strip
                 @interfaces[@int_name] = {"hwaddr" => (line =~ /(HWaddr)/ ? ($') : "nil").strip.chomp }
               else
                 @interfaces[@int_name]["inet_addr"] = (line =~ /inet addr:(\S+)/ ? ($1) : "nil") if line =~ /inet addr:/
                 @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? ($1) : "nil") if line =~ /Bcast:/
                 @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? ($1) : "nil") if line =~ /Mask:/
                 @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? ($1) : "nil") if line =~ /MTU:/
                 @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? ($1) : "nil") if line =~ /Metric:/
               end
   
               if @interfaces.has_key?(@new_resource.device)
                 @interface = @interfaces.fetch(@new_resource.device)
   
                 @current_resource.target(@new_resource.target)
                 @current_resource.device(@int_name)
                 @current_resource.inet_addr(@interface["inet_addr"])
                 @current_resource.hwaddr(@interface["hwaddr"])
                 @current_resource.bcast(@interface["bcast"])
                 @current_resource.mask(@interface["mask"])
                 @current_resource.mtu(@interface["mtu"])
                 @current_resource.metric(@interface["metric"])
               end
             end
           end
   
           unless status.exitstatus == 0
             raise Chef::Exception::Ifconfig, "ifconfig failed - #{status.inspect}!"
           end
   
           @current_resource
         end
   
         def action_add
           # check to see if load_current_resource found ifconfig
           unless @current_resource.inet_addr
             unless @new_resource.device == "lo"
               command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
               command << " netmask #{@new_resource.mask}" if @new_resource.mask
               command << " metric #{@new_resource.metric}" if @new_resource.metric
               command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
             end
   
             run_command(
               :command => command
             )
             Chef::Log.info("#{@new_resource} added")
             @new_resource.updated_by_last_action(true)
           end
   
           # Write out the config files
           generate_config
         end
   
         def action_enable
           # check to see if load_current_resource found ifconfig
           # enables, but does not manage config files
           unless @current_resource.inet_addr
             unless @new_resource.device == "lo"
               command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
               command << " netmask #{@new_resource.mask}" if @new_resource.mask
               command << " metric #{@new_resource.metric}" if @new_resource.metric
               command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
             end
   
             run_command(
               :command => command
             )
             Chef::Log.info("#{@new_resource} enabled")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def action_delete
           # check to see if load_current_resource found the interface
           if @current_resource.device
             command = "ifconfig #{@new_resource.device} down"
             run_command(
               :command => command
             )
             delete_config
             Chef::Log.info("#{@new_resource} deleted")
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug("#{@new_resource} does not exist - nothing to do")
           end
         end
   
         def action_disable
           # check to see if load_current_resource found the interface
           # disables, but leaves config files in place.
           if @current_resource.device
             command = "ifconfig #{@new_resource.device} down"
             run_command(
               :command => command
             )
             Chef::Log.info("#{@new_resource} disabled")
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug("#{@new_resource} does not exist - nothing to do")
           end
         end
   
         def generate_config
           b = binding
           case node[:platform]
           when "centos","redhat","fedora"
             content = %{
   <% if @new_resource.device %>DEVICE=<%= @new_resource.device %><% end %>
   <% if @new_resource.onboot %>ONBOOT=<%= @new_resource.onboot %><% end %>
   <% if @new_resource.bootproto %>BOOTPROTO=<%= @new_resource.bootproto %><% end %>
   <% if @new_resource.target %>IPADDR=<%= @new_resource.target %><% end %>
   <% if @new_resource.mask %>NETMASK=<%= @new_resource.mask %><% end %>
   <% if @new_resource.network %>NETWORK=<%= @new_resource.network %><% end %>
   <% if @new_resource.bcast %>BROADCAST=<%= @new_resource.bcast %><% end %>
   <% if @new_resource.onparent %>ONPARENT=<%= @new_resource.onparent %><% end %>
             }
             template = ::ERB.new(content)
             network_file = ::File.new("/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}", "w")
             network_file.puts(template.result(b))
             network_file.close
             Chef::Log.info("#{@new_resource} created configuration file")
           when "debian","ubuntu"
             # template
           when "slackware"
             # template
           end
         end
   
         def delete_config
           require 'fileutils'
           case node[:platform]
           when "centos","redhat","fedora"
             ifcfg_file = "/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}"
             if ::File.exist?(ifcfg_file)
               FileUtils.rm_f(ifcfg_file, :verbose => false, :force => true)
             end
           when "debian","ubuntu"
             # delete configs
           when "slackware"
             # delete configs
           end
         end
   
       end
     end
   end

lib/chef/shell_out/unix.rb

   class Chef
     class ShellOut
       module Unix
   
         # Run the command, writing the command's standard out and standard error
         # to +stdout+ and +stderr+, and saving its exit status object to +status+
         # === Returns
         # returns   +self+; +stdout+, +stderr+, +status+, and +exitstatus+ will be
         # populated with results of the command
         # === Raises
         # * Errno::EACCES  when you are not privileged to execute the command
         # * Errno::ENOENT  when the command is not available on the system (or not
         #   in the current $PATH)
         # * Chef::Exceptions::CommandTimeout  when the command does not complete
         #   within +timeout+ seconds (default: 60s)
         def run_command
           @child_pid = fork_subprocess
   
           configure_parent_process_file_descriptors
           propagate_pre_exec_failure
   
           @result = nil
           @execution_time = 0
   
           # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
           # when calling IO.select and IO#read. Some OS Vendors are not interested
           # in updating their ruby packages (Apple, *cough*) and we *have to*
           # make it work. So I give you this epic hack:
           GC.disable
           until @status
             ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
             unless ready
               @execution_time += READ_WAIT_TIME
               if @execution_time >= timeout && !@result
                 raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
               end
             end
   
             if ready && ready.first.include?(child_stdout)
               read_stdout_to_buffer
             end
             if ready && ready.first.include?(child_stderr)
               read_stderr_to_buffer
             end
   
             unless @status
               # make one more pass to get the last of the output after the
               # child process dies
               if results = Process.waitpid2(@child_pid, Process::WNOHANG)
                 @status = results.last
                 redo
               end
             end
           end
           self
         rescue Exception
           # do our best to kill zombies
           Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
           raise
         ensure
           # no matter what happens, turn the GC back on, and hope whatever busted
           # version of ruby we're on doesn't allocate some objects during the next
           # GC run.
           GC.enable
           close_all_pipes
         end
   
         private
   
         def set_user
           if user
             Process.euid = uid
             Process.uid = uid
           end
         end
   
         def set_group
           if group
             Process.egid = gid
             Process.gid = gid
           end
         end
   
         def set_environment
           environment.each do |env_var,value|
             ENV[env_var] = value
           end
         end
   
         def set_umask
           File.umask(umask) if umask
         end
   
         def set_cwd
           Dir.chdir(cwd) if cwd
         end
   
         def initialize_ipc
           @stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
           @process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
         end
   
         def child_stdout
           @stdout_pipe[0]
         end
   
         def child_stderr
           @stderr_pipe[0]
         end
   
         def child_process_status
           @process_status_pipe[0]
         end
   
         def close_all_pipes
           child_stdout.close  unless child_stdout.closed?
           child_stderr.close  unless child_stderr.closed?
           child_process_status.close unless child_process_status.closed?
         end
   
         # replace stdout, and stderr with pipes to the parent, and close the
         # reader side of the error marshaling side channel. Close STDIN so when we
         # exec, the new program will know it's never getting input ever.
         def configure_subprocess_file_descriptors
           process_status_pipe.first.close
   
           # HACK: for some reason, just STDIN.close isn't good enough when running
           # under ruby 1.9.2, so make it good enough:
           stdin_reader, stdin_writer = IO.pipe
           stdin_writer.close
           STDIN.reopen stdin_reader
           stdin_reader.close
   
           stdout_pipe.first.close
           STDOUT.reopen stdout_pipe.last
           stdout_pipe.last.close
   
           stderr_pipe.first.close
           STDERR.reopen stderr_pipe.last
           stderr_pipe.last.close
   
           STDOUT.sync = STDERR.sync = true
         end
   
         def configure_parent_process_file_descriptors
           # Close the sides of the pipes we don't care about
           stdout_pipe.last.close
           stderr_pipe.last.close
           process_status_pipe.last.close
           # Get output as it happens rather than buffered
           child_stdout.sync = true
           child_stderr.sync = true
   
           true
         end
   
         # Some patch levels of ruby in wide use (in particular the ruby 1.8.6 on OSX)
         # segfault when you IO.select a pipe that's reached eof. Weak sauce.
         def open_pipes
           @open_pipes ||= [child_stdout, child_stderr]
         end
   
         def read_stdout_to_buffer
           while chunk = child_stdout.read_nonblock(READ_SIZE)
             @stdout << chunk
             @live_stream << chunk if @live_stream
           end
         rescue Errno::EAGAIN
         rescue EOFError
           open_pipes.delete_at(0)
         end
   
         def read_stderr_to_buffer
           while chunk = child_stderr.read_nonblock(READ_SIZE)
             @stderr << chunk
           end
         rescue Errno::EAGAIN
         rescue EOFError
           open_pipes.delete_at(1)
         end
   
         def fork_subprocess
           initialize_ipc
   
           fork do
             configure_subprocess_file_descriptors
   
             set_group
             set_user
             set_environment
             set_umask
             set_cwd
   
             begin
               command.kind_of?(Array) ? exec(*command) : exec(command)
   
               raise 'forty-two' # Should never get here
             rescue Exception => e
               Marshal.dump(e, process_status_pipe.last)
               process_status_pipe.last.flush
             end
             process_status_pipe.last.close unless (process_status_pipe.last.closed?)
             exit!
           end
         end
   
         # Attempt to get a Marshaled error from the side-channel.
         # If it's there, un-marshal it and raise. If it's not there,
         # assume everything went well.
         def propagate_pre_exec_failure
           begin
             e = Marshal.load child_process_status
             raise(Exception === e ? e : "unknown failure: #{e.inspect}")
           rescue EOFError # If we get an EOF error, then the exec was successful
             true
           ensure
             child_process_status.close
           end
         end
   
       end
     end
   end

lib/chef/provider/deploy.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require "chef/mixin/command"
   require "chef/mixin/from_file"
   require "chef/provider/git"
   require "chef/provider/subversion"
   
   class Chef
     class Provider
       class Deploy < Chef::Provider
   
         include Chef::Mixin::FromFile
         include Chef::Mixin::Command
   
         attr_reader :scm_provider, :release_path, :previous_release_path
   
         def initialize(new_resource, run_context)
           super(new_resource, run_context)
   
           @scm_provider = new_resource.scm_provider.new(new_resource, run_context)
   
           # @configuration is not used by Deploy, it is only for backwards compat with
           # chef-deploy or capistrano hooks that might use it to get environment information
           @configuration = @new_resource.to_hash
           @configuration[:environment] = @configuration[:environment] && @configuration[:environment]["RAILS_ENV"]
         end
   
         def load_current_resource
           @release_path = @new_resource.deploy_to + "/releases/#{release_slug}"
         end
   
         def sudo(command,&block)
           execute(command, &block)
         end
   
         def run(command, &block)
           exec = execute(command, &block)
           exec.user(@new_resource.user) if @new_resource.user
           exec.group(@new_resource.group) if @new_resource.group
           exec.cwd(release_path) unless exec.cwd
           exec.environment(@new_resource.environment) unless exec.environment
           exec
         end
   
         def action_deploy
           save_release_state
   
           if deployed?(release_path)
             if current_release?(release_path) 
               Chef::Log.debug("#{@new_resource} is the latest version")
             else
               action_rollback
             end
           else
   
             with_rollback_on_error do
               deploy
               @new_resource.updated_by_last_action(true)
             end
           end
         end
   
         def action_force_deploy
           if deployed?(release_path)
             Chef::Log.info("Already deployed app at #{release_path}, forcing.")
             FileUtils.rm_rf(release_path)
             Chef::Log.info("#{@new_resource} forcing deploy of already deployed app at #{release_path}")
           end
   
           # Alternatives:
           # * Move release_path directory before deploy and move it back when error occurs
           # * Rollback to previous commit
           # * Do nothing - because deploy is force, it will be retried in short time
           # Because last is simpliest, keep it
           deploy
           @new_resource.updated_by_last_action(true)
         end
   
         def action_rollback
           if release_path
             rp_index = all_releases.index(release_path)
             raise RuntimeError, "There is no release to rollback to!" unless rp_index
             rp_index += 1
             releases_to_nuke = all_releases[rp_index..-1]
           else
             @release_path = all_releases[-2]
             raise RuntimeError, "There is no release to rollback to!" unless @release_path
             releases_to_nuke = [ all_releases.last ]
           end
   
           rollback
   
           releases_to_nuke.each do |i|
             Chef::Log.info "#{@new_resource} removing release: #{i}"
             FileUtils.rm_rf i
             release_deleted(i)
           end
           @new_resource.updated_by_last_action(true)
         end
   
         def deploy
           enforce_ownership
           verify_directories_exist
           update_cached_repo
           copy_cached_repo
           install_gems
           enforce_ownership
           callback(:before_migrate, @new_resource.before_migrate)
           migrate
           callback(:before_symlink, @new_resource.before_symlink)
           symlink
           callback(:before_restart, @new_resource.before_restart)
           restart
           callback(:after_restart, @new_resource.after_restart)
           cleanup!
           Chef::Log.info "#{@new_resource} deployed to #{@new_resource.deploy_to}"
         end
   
         def rollback
           Chef::Log.info "#{@new_resource} rolling back to previous release #{release_path}"
           symlink
           Chef::Log.info "#{@new_resource} restarting with previous release"
           restart
         end
   
         def callback(what, callback_code=nil)
           @collection = Chef::ResourceCollection.new
           case callback_code
           when Proc
             Chef::Log.info "#{@new_resource} running callback #{what}"
             recipe_eval(&callback_code)
           when String
             callback_file = "#{release_path}/#{callback_code}"
             unless ::File.exist?(callback_file)
               raise RuntimeError, "Can't find your callback file #{callback_file}"
             end
             run_callback_from_file(callback_file)
           when nil
             run_callback_from_file("#{release_path}/deploy/#{what}.rb")
           else
             raise RuntimeError, "You gave me a callback I don't know what to do with: #{callback_code.inspect}"
           end
         end
   
         def migrate
           run_symlinks_before_migrate
   
           if @new_resource.migrate
             enforce_ownership
   
             environment = @new_resource.environment
             env_info = environment && environment.map do |key_and_val|
               "#{key_and_val.first}='#{key_and_val.last}'"
             end.join(" ")
   
             Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
             run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :command_log_level => :info))
           end
         end
   
         def symlink
           purge_tempfiles_from_current_release
           link_tempfiles_to_current_release
           link_current_release_to_production
           Chef::Log.info "#{@new_resource} updated symlinks"
         end
   
         def restart
           if restart_cmd = @new_resource.restart_command
             if restart_cmd.kind_of?(Proc)
               Chef::Log.info("#{@new_resource} restarting app with embedded recipe")
               recipe_eval(&restart_cmd)
             else
               Chef::Log.info("#{@new_resource} restarting app")
               run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path))
             end
           end
         end
   
         def cleanup!
           all_releases[0..-6].each do |old_release|
             Chef::Log.info "#{@new_resource} removing old release #{old_release}"
             FileUtils.rm_rf(old_release)
             release_deleted(old_release)
           end
         end
   
         def all_releases
           Dir.glob(@new_resource.deploy_to + "/releases/*").sort
         end
   
         def update_cached_repo
           @scm_provider.load_current_resource
           if @new_resource.svn_force_export
             svn_force_export
           else
             run_scm_sync
           end
         end
   
         def run_scm_sync
           @scm_provider.action_sync
         end
   
         def svn_force_export
           Chef::Log.info "#{@new_resource} exporting source repository"
           @scm_provider.action_force_export
         end
   
         def copy_cached_repo
           FileUtils.mkdir_p(@new_resource.deploy_to + "/releases")
           run_command(:command => "cp -RPp #{::File.join(@new_resource.destination, ".")} #{release_path}")
           Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}"
           release_created(release_path)
         end
   
         def enforce_ownership
           FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to)
           Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
           Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
         end
   
         def verify_directories_exist
           create_dir_unless_exists(@new_resource.deploy_to)
           create_dir_unless_exists(@new_resource.shared_path)
         end
   
         def link_current_release_to_production
           FileUtils.rm_f(@new_resource.current_path)
           begin
             FileUtils.ln_sf(release_path, @new_resource.current_path)
           rescue => e
             raise Chef::Exceptions::FileNotFound.new("Cannot symlink current release to production: #{e.message}")
           end
           Chef::Log.info "#{@new_resource} linked release #{release_path} into production at #{@new_resource.current_path}"
           enforce_ownership
         end
   
         def run_symlinks_before_migrate
           links_info = @new_resource.symlink_before_migrate.map { |src, dst| "#{src} => #{dst}" }.join(", ")
           @new_resource.symlink_before_migrate.each do |src, dest|
             begin
               FileUtils.ln_sf(@new_resource.shared_path + "/#{src}", release_path + "/#{dest}")
             rescue => e
               raise Chef::Exceptions::FileNotFound.new("Cannot symlink #{@new_resource.shared_path}/#{src} to #{release_path}/#{dest} before migrate: #{e.message}")
             end
           end
           Chef::Log.info "#{@new_resource} made pre-migration symlinks"
         end
   
         def link_tempfiles_to_current_release
           dirs_info = @new_resource.create_dirs_before_symlink.join(",")
           @new_resource.create_dirs_before_symlink.each do |dir| 
             create_dir_unless_exists(release_path + "/#{dir}")
           end
           Chef::Log.info("#{@new_resource} created directories before symlinking #{dirs_info}")
   
           links_info = @new_resource.symlinks.map { |src, dst| "#{src} => #{dst}" }.join(", ")
           @new_resource.symlinks.each do |src, dest|
             create_dir_unless_exists(::File.join(@new_resource.shared_path, src))
             begin
               FileUtils.ln_sf(::File.join(@new_resource.shared_path, src), ::File.join(release_path, dest))
             rescue => e
               raise Chef::Exceptions::FileNotFound.new("Cannot symlink shared data #{::File.join(@new_resource.shared_path, src)} to #{::File.join(release_path, dest)}: #{e.message}")
             end
           end
           Chef::Log.info("#{@new_resource} linked shared paths into current release: #{links_info}")
           run_symlinks_before_migrate
           enforce_ownership
         end
   
         def create_dirs_before_symlink
         end
   
         def purge_tempfiles_from_current_release
           log_info = @new_resource.purge_before_symlink.join(", ")
           @new_resource.purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") }
           Chef::Log.info("#{@new_resource} purged directories in checkout #{log_info}")
         end
   
         protected
   
         # Internal callback, called after copy_cached_repo.
         # Override if you need to keep state externally.
         def release_created(release_path)
         end
   
         # Internal callback, called during cleanup! for each old release removed.
         # Override if you need to keep state externally.
         def release_deleted(release_path)
         end
   
         def release_slug
           raise Chef::Exceptions::Override, "You must override release_slug in #{self.to_s}"
         end
   
         def install_gems
           gem_resource_collection_runner.converge
         end
   
         def gem_resource_collection_runner
           gems_collection = Chef::ResourceCollection.new
           gem_packages.each { |rbgem| gems_collection << rbgem }
           gems_run_context = run_context.dup
           gems_run_context.resource_collection = gems_collection
           Chef::Runner.new(gems_run_context)
         end
   
         def gem_packages
           return [] unless ::File.exist?("#{release_path}/gems.yml")
           gems = YAML.load(IO.read("#{release_path}/gems.yml"))
   
           gems.map do |g|
             r = Chef::Resource::GemPackage.new(g[:name], run_context)
             r.version g[:version]
             r.action :install
             r.source "http://gems.github.com"
             r
           end
         end
   
         def run_options(run_opts={})
           run_opts[:user] = @new_resource.user if @new_resource.user
           run_opts[:group] = @new_resource.group if @new_resource.group
           run_opts[:environment] = @new_resource.environment if @new_resource.environment
           run_opts[:command_log_prepend] = @new_resource.to_s
           run_opts[:command_log_level] ||= :debug
           if run_opts[:command_log_level] == :info
             if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
               run_opts[:live_stream] = STDOUT
             end
           end
           run_opts
         end
   
         def run_callback_from_file(callback_file)
           if ::File.exist?(callback_file)
             Dir.chdir(release_path) do
               Chef::Log.info "#{@new_resource} running deploy hook #{callback_file}"
               recipe_eval { from_file(callback_file) }
             end
           end
         end
   
         def create_dir_unless_exists(dir)
           if ::File.directory?(dir)
             Chef::Log.debug "#{@new_resource} not creating #{dir} because it already exists"
             return false
           end
   
           begin
             FileUtils.mkdir_p(dir)
             Chef::Log.debug "#{@new_resource} created directory #{dir}"
             if @new_resource.user
               FileUtils.chown(@new_resource.user, nil, dir)
               Chef::Log.debug("#{@new_resource} set user to #{@new_resource.user} for #{dir}")
             end
             if @new_resource.group
               FileUtils.chown(nil, @new_resource.group, dir)
               Chef::Log.debug("#{@new_resource} set group to #{@new_resource.group} for #{dir}")
             end
           rescue => e
             raise Chef::Exceptions::FileNotFound.new("Cannot create directory #{dir}: #{e.message}")
           end
         end
   
         def with_rollback_on_error
           yield
         rescue ::Exception => e
           if @new_resource.rollback_on_error
             Chef::Log.warn "Error on deploying #{release_path}: #{e.message}" 
             failed_release = release_path
           
             if previous_release_path
               @release_path = previous_release_path
               rollback
             end
   
             Chef::Log.info "Removing failed deploy #{failed_release}"
             FileUtils.rm_rf failed_release
             release_deleted(failed_release)
           end
           
           raise
         end
   
         def save_release_state
           if ::File.exists?(@new_resource.current_path)
             release = ::File.readlink(@new_resource.current_path)
             @previous_release_path = release if ::File.exists?(release)
           end
         end
   
         def deployed?(release)
           all_releases.include?(release)
         end
   
         def current_release?(release)
           @previous_release_path == release
         end
       end
     end
   end

lib/chef/provider/package/zypper.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'singleton'
   
   class Chef
     class Provider
       class Package
         class Zypper < Chef::Provider::Package  
         
    
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
   
             is_installed=false
             is_out_of_date=false
             version=''
             oud_version=''
             Chef::Log.debug("#{@new_resource} checking zypper")
             status = popen4("zypper info #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                 when /^Version: (.+)$/
                   version = $1
                   Chef::Log.debug("#{@new_resource} version #{$1}")
                 when /^Installed: Yes$/
                   is_installed=true
                   Chef::Log.debug("#{@new_resource} is installed")
                   
                 when /^Installed: No$/
                   is_installed=false
                   Chef::Log.debug("#{@new_resource} is not installed")
                 when /^Status: out-of-date \(version (.+) installed\)$/
                   is_out_of_date=true
                   oud_version=$1
                   Chef::Log.debug("#{@new_resource} out of date version #{$1}")
                 end
               end
             end
   
             if is_installed==false
               @candidate_version=version
               @current_resource.version(nil)
             end
    
             if is_installed==true
               if is_out_of_date==true
                 @current_resource.version(oud_version)
                 @candidate_version=version
               else 
                 @current_resource.version(version)
                 @candidate_version=version
               end
             end
   
             unless status.exitstatus == 0
               raise Chef::Exceptions::Package, "zypper failed - #{status.inspect}!"
             end
             
             @current_resource
           end
           
           #Gets the zypper Version from command output (Returns Floating Point number)
           def zypper_version()
             `zypper -V 2>&1`.scan(/\d+/).join(".").to_f
           end
   
           def install_package(name, version)
             if zypper_version < 1.0
               run_command(
                 :command => "zypper install -y #{name}"
               )
             elsif version
               run_command(
                 :command => "zypper -n --no-gpg-checks install -l  #{name}=#{version}"
               )
             else
               run_command(
                 :command => "zypper -n --no-gpg-checks install -l  #{name}"
               )
             end
           end
   
           def upgrade_package(name, version)
             if zypper_version < 1.0
               run_command(
                 :command => "zypper install -y #{name}"
               )
             elsif version
               run_command(
                 :command => "zypper -n --no-gpg-checks install -l #{name}=#{version}"
               )
             else
               run_command(
                 :command => "zypper -n --no-gpg-checks install -l #{name}"
               )
             end
           end
   
           def remove_package(name, version)
             if zypper_version < 1.0
               run_command(
                 :command => "zypper remove -y #{name}"
               )
             elsif version
               run_command(
                 :command => "zypper -n --no-gpg-checks remove  #{name}=#{version}"
               )
             else
               run_command(
                 :command => "zypper -n --no-gpg-checks remove  #{name}"
               )
             end
               
            
           end
         
           def purge_package(name, version)
             remove_package(name, version)
           end
         
         end
       end
     end
   end

lib/chef/resource/cron.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Cron < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :cron
           @action = :create
           @allowed_actions.push(:create, :delete)
           @minute = "*"
           @hour = "*"
           @day = "*"
           @month = "*"
           @weekday = "*"
           @command = nil
           @user = "root"
           @mailto = nil
           @path = nil
           @shell = nil
           @home = nil
         end
   
         def minute(arg=nil)
           if arg.is_a?(Integer)
             converted_arg = arg.to_s
           else
             converted_arg = arg
           end
           begin
             if integerize(arg) > 59 then raise RangeError end
           rescue ArgumentError
           end
           set_or_return(
             :minute,
             converted_arg,
             :kind_of => String
           )
         end
   
         def hour(arg=nil)
           if arg.is_a?(Integer)
             converted_arg = arg.to_s
           else
             converted_arg = arg
           end
           begin
             if integerize(arg) > 23 then raise RangeError end
           rescue ArgumentError
           end
           set_or_return(
             :hour,
             converted_arg,
             :kind_of => String
           )
         end
   
         def day(arg=nil)
           if arg.is_a?(Integer)
             converted_arg = arg.to_s
           else
             converted_arg = arg
           end
           begin
             if integerize(arg) > 31 then raise RangeError end
           rescue ArgumentError
           end
           set_or_return(
             :day,
             converted_arg,
             :kind_of => String
           )
         end
   
         def month(arg=nil)
           if arg.is_a?(Integer)
             converted_arg = arg.to_s
           else
             converted_arg = arg
           end
           begin
             if integerize(arg) > 12 then raise RangeError end
           rescue ArgumentError
           end
           set_or_return(
             :month,
             converted_arg,
             :kind_of => String
           )
         end
   
         def weekday(arg=nil)
           if arg.is_a?(Integer)
             converted_arg = arg.to_s
           else
             converted_arg = arg
           end
           begin
             if integerize(arg) > 7 then raise RangeError end
           rescue ArgumentError
           end
           set_or_return(
             :weekday,
             converted_arg,
             :kind_of => String
           )
         end
   
         def mailto(arg=nil)
           set_or_return(
             :mailto,
             arg,
             :kind_of => String
           )
         end
   
         def path(arg=nil)
           set_or_return(
             :path,
             arg,
             :kind_of => String
           )
         end
   
         def home(arg=nil)
           set_or_return(
             :home,
             arg,
             :kind_of => String
           )
         end
   
         def shell(arg=nil)
           set_or_return(
             :shell,
             arg,
             :kind_of => String
           )
         end
   
         def command(arg=nil)
           set_or_return(
             :command,
             arg,
             :kind_of => String
           )
         end
   
         def user(arg=nil)
           set_or_return(
             :user,
             arg,
             :kind_of => String
           )
         end
         
         private
         
         # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no.
         def integerize(integerish)
           Integer(integerish)
         rescue TypeError
           0
         end
       end
     end
   end
   
   

lib/chef/resource.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/params_validate'
   require 'chef/mixin/check_helper'
   require 'chef/mixin/language'
   require 'chef/mixin/convert_to_class_name'
   require 'chef/mixin/command'
   require 'chef/resource_collection'
   require 'chef/node'
   
   require 'chef/mixin/deprecation'
   
   class Chef
     class Resource
       class Notification < Struct.new(:resource, :action, :notifying_resource)
   
         def duplicates?(other_notification)
           unless other_notification.respond_to?(:resource) && other_notification.respond_to?(:action)
             msg = "only duck-types of Chef::Resource::Notification can be checked for duplication "\
                   "you gave #{other_notification.inspect}"
             raise ArgumentError, msg
           end
           other_notification.resource == resource && other_notification.action == action
         end
   
         def resolve_resource_reference(resource_collection)
           return resource if resource.kind_of?(Chef::Resource)
   
           matching_resource = resource_collection.find(resource)
           if Array(matching_resource).size > 1
             msg = "Notification #{self} from #{notifying_resource} was created with a reference to multiple resources, "\
                   "but can only notify one resource. Notifying resource was defined on #{notifying_resource.source_line}"
             raise Chef::Exceptions::InvalidResourceReference, msg
           end
           self.resource = matching_resource
         rescue Chef::Exceptions::ResourceNotFound => e
           err = Chef::Exceptions::ResourceNotFound.new(<<-FAIL)
   Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
   but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \
   #{notifying_resource.source_line}
   FAIL
           err.set_backtrace(e.backtrace)
           raise err
         rescue Chef::Exceptions::InvalidResourceSpecification => e
             err = Chef::Exceptions::InvalidResourceSpecification.new(<<-F)
   Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \
   but #{resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \
   is defined near #{notifying_resource.source_line}
   F
             err.set_backtrace(e.backtrace)
           raise err
         end
   
       end
   
       FORBIDDEN_IVARS = [:@run_context, :@node]
       HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@node]
   
       include Chef::Mixin::CheckHelper
       include Chef::Mixin::ParamsValidate
       include Chef::Mixin::Language
       include Chef::Mixin::ConvertToClassName
       include Chef::Mixin::Deprecation
   
       attr_accessor :params
       attr_accessor :provider
       attr_accessor :allowed_actions
       attr_accessor :run_context
       attr_accessor :cookbook_name
       attr_accessor :recipe_name
       attr_accessor :enclosing_provider
       attr_accessor :source_line
       attr_accessor :retries
       attr_accessor :retry_delay
   
       attr_reader :updated
   
       attr_reader :resource_name
       attr_reader :not_if_args
       attr_reader :only_if_args
   
       # Each notify entry is a resource/action pair, modeled as an
       # Struct with a #resource and #action member
       attr_reader :immediate_notifications
       attr_reader :delayed_notifications
   
       def initialize(name, run_context=nil)
         @name = name
         @run_context = run_context
         @noop = nil
         @before = nil
         @params = Hash.new
         @provider = nil
         @allowed_actions = [ :nothing ]
         @action = :nothing
         @updated = false
         @updated_by_last_action = false
         @supports = {}
         @ignore_failure = false
         @retries = 0
         @retry_delay = 2
         @not_if = nil
         @not_if_args = {}
         @only_if = nil
         @only_if_args = {}
         @immediate_notifications = Array.new
         @delayed_notifications = Array.new
         @source_line = nil
   
         @node = run_context ? deprecated_ivar(run_context.node, :node, :warn) : nil
       end
   
       def updated=(true_or_false)
         Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.")
         Chef::Log.warn("Called from:")
         caller[0..3].each {|line| Chef::Log.warn(line)}
         updated_by_last_action(true_or_false)
         @updated = true_or_false
       end
   
       def node
         run_context && run_context.node
       end
   
       # If an unknown method is invoked, determine whether the enclosing Provider's
       # lexical scope can fulfill the request. E.g. This happens when the Resource's
       # block invokes new_resource.
       def method_missing(method_symbol, *args, &block)
         if enclosing_provider && enclosing_provider.respond_to?(method_symbol)
           enclosing_provider.send(method_symbol, *args, &block)
         else
           raise NoMethodError, "undefined method `#{method_symbol.to_s}' for #{self.class.to_s}"
         end
       end
   
       def load_prior_resource
         begin
           prior_resource = run_context.resource_collection.lookup(self.to_s)
           Chef::Log.debug("Setting #{self.to_s} to the state of the prior #{self.to_s}")
           prior_resource.instance_variables.each do |iv|
             unless iv.to_sym == :@source_line || iv.to_sym == :@action
               self.instance_variable_set(iv, prior_resource.instance_variable_get(iv))
             end
           end
           true
         rescue Chef::Exceptions::ResourceNotFound => e
           true
         end
       end
   
       def supports(args={})
         if args.any?
           @supports = args
         else
           @supports
         end
       end
   
       def provider(arg=nil)
         klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
                   lookup_provider_constant(arg)
                 else
                   arg
                 end
         set_or_return(
           :provider,
           klass,
           :kind_of => [ Class ]
         )
       end
   
       def action(arg=nil)
         if arg
           action_list = arg.kind_of?(Array) ? arg : [ arg ]
           action_list = action_list.collect { |a| a.to_sym }
           action_list.each do |action|
             validate(
               {
                 :action => action,
               },
               {
                 :action => { :kind_of => Symbol, :equal_to => @allowed_actions },
               }
             )
           end
           @action = action_list
         else
           @action
         end
       end
   
       def name(name=nil)
         set_if_args(@name, name) do
           raise ArgumentError, "name must be a string!" unless name.kind_of?(String)
           @name = name
         end
       end
   
       def noop(tf=nil)
         set_if_args(@noop, tf) do
           raise ArgumentError, "noop must be true or false!" unless tf == true || tf == false
           @noop = tf
         end
       end
   
       def ignore_failure(arg=nil)
         set_or_return(
           :ignore_failure,
           arg,
           :kind_of => [ TrueClass, FalseClass ]
         )
       end
   
       def retries(arg=nil)
         set_or_return(
           :retries,
           arg,
           :kind_of => Integer
         )
       end
   
       def retry_delay(arg=nil)
         set_or_return(
           :retry_delay,
           arg,
           :kind_of => Integer
         )
       end
   
       def epic_fail(arg=nil)
         ignore_failure(arg)
       end
   
       def notifies(*args)
         unless ( args.size > 0 && args.size < 4)
           raise ArgumentError, "Wrong number of arguments for notifies: should be 1-3 arguments, you gave #{args.inspect}"
         end
   
         if args.size > 1 # notifies(:action, resource) OR notifies(:action, resource, :immediately)
           add_notification(*args)
         else
           # This syntax is so weird. surely people will just give us one hash?
           notifications = args.flatten
           notifications.each do |resources_notifications|
             resources_notifications.each do |resource, notification|
               action, timing = notification[0], notification[1]
               Chef::Log.debug "Adding notification from resource #{self} to `#{resource.inspect}' => `#{notification.inspect}'"
               add_notification(action, resource, timing)
             end
           end
         end
       rescue NoMethodError
         Chef::Log.fatal("Error processing notifies(#{args.inspect}) on #{self}")
         raise
       end
   
       def add_notification(action, resources, timing=:delayed)
         resources = [resources].flatten
         resources.each do |resource|
           case timing.to_s
           when 'delayed'
             notifies_delayed(action, resource)
           when 'immediate', 'immediately'
             notifies_immediately(action, resource)
           else
             raise ArgumentError,  "invalid timing: #{timing} for notifies(#{action}, #{resources.inspect}, #{timing}) resource #{self} "\
                                   "Valid timings are: :delayed, :immediate, :immediately"
           end
         end
   
         true
       end
   
       # Iterates over all immediate and delayed notifications, calling
       # resolve_resource_reference on each in turn, causing them to
       # resolve lazy/forward references.
       def resolve_notification_references
         @immediate_notifications.each { |n| n.resolve_resource_reference(run_context.resource_collection) }
         @delayed_notifications.each {|n| n.resolve_resource_reference(run_context.resource_collection) }
       end
   
       def notifies_immediately(action, resource_spec)
         @immediate_notifications << Notification.new(resource_spec, action, self)
       end
   
       def notifies_delayed(action, resource_spec)
         @delayed_notifications << Notification.new(resource_spec, action, self)
       end
   
       def resources(*args)
         run_context.resource_collection.find(*args)
       end
   
       def subscribes(action, resources, timing=:delayed)
         resources = [resources].flatten
         resources.each do |resource|
           resource.notifies(action, self, timing)
         end
         true
       end
   
       def is(*args)
         if args.size == 1
           args.first
         else
           return *args
         end
       end
   
       def to_s
         "#{@resource_name}[#{@name}]"
       end
   
       def to_text
         ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS
         text = "# Declared in #{@source_line}\n"
         text << convert_to_snake_case(self.class.name, 'Chef::Resource') + "(\"#{name}\") do\n"
         ivars.each do |ivar|
           if (value = instance_variable_get(ivar)) && !(value.respond_to?(:empty?) && value.empty?)
             text << "  #{ivar.to_s.sub(/^@/,'')}(#{value.inspect})\n"
           end
         end
         text << "end\n"
       end
   
       def inspect
         ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
         ivars.inject("<#{to_s}") do |str, ivar|
           str << " #{ivar}: #{instance_variable_get(ivar).inspect}"
         end << ">"
       end
   
       # Serialize this object as a hash
       def to_json(*a)
         safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
         instance_vars = Hash.new
         safe_ivars.each do |iv|
           instance_vars[iv.to_s.sub(/^@/, '')] = instance_variable_get(iv)
         end
         results = {
           'json_class' => self.class.name,
           'instance_vars' => instance_vars
         }
         results.to_json(*a)
       end
   
       def to_hash
         safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS
         instance_vars = Hash.new
         safe_ivars.each do |iv|
           key = iv.to_s.sub(/^@/,'').to_sym
           instance_vars[key] = instance_variable_get(iv)
         end
         instance_vars
       end
   
       def only_if(arg=nil, args = {}, &blk)
         if Kernel.block_given?
           @only_if = blk
           @only_if_args = args
         else
           @only_if = arg if arg
           @only_if_args = args if arg
         end
         @only_if
       end
   
       def not_if(arg=nil, args = {}, &blk)
         if Kernel.block_given?
           @not_if = blk
           @not_if_args = args
         else
           @not_if = arg if arg
           @not_if_args = args if arg
         end
         @not_if
       end
   
       def defined_at
         if cookbook_name && recipe_name && source_line
           "#{cookbook_name}::#{recipe_name} line #{source_line.split(':')[1]}"
         elsif source_line
           file, line_no = source_line.split(':')
           "#{file} line #{line_no}"
         else
           "dynamically defined"
         end
       end
   
       def run_action(action)
         if Chef::Config[:verbose_logging] || Chef::Log.level == :debug
           # This can be noisy
           Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
         end
   
         # ensure that we don't leave @updated_by_last_action set to true
         # on accident
         updated_by_last_action(false)
   
         begin
           # Check if this resource has an only_if block -- if it does,
           # evaluate the only_if block and skip the resource if
           # appropriate.
           if only_if
             unless Chef::Mixin::Command.only_if(only_if, only_if_args)
               Chef::Log.debug("Skipping #{self} due to only_if")
               return
             end
           end
   
           # Check if this resource has a not_if block -- if it does,
           # evaluate the not_if block and skip the resource if
           # appropriate.
           if not_if
             unless Chef::Mixin::Command.not_if(not_if, not_if_args)
               Chef::Log.debug("Skipping #{self} due to not_if")
               return
             end
           end
   
           provider = Chef::Platform.provider_for_resource(self)
           provider.load_current_resource
           provider.send("action_#{action}")
         rescue => e
           if ignore_failure
             Chef::Log.error("#{self} (#{defined_at}) had an error: #{e.message}")
           else
             Chef::Log.error("#{self} (#{defined_at}) has had an error")
             new_exception = e.exception("#{self} (#{defined_at}) had an error: #{e.message}")
             new_exception.set_backtrace(e.backtrace)
             raise new_exception
           end
         end
       end
   
       def updated_by_last_action(true_or_false)
         @updated ||= true_or_false
         @updated_by_last_action = true_or_false
       end
   
       def updated_by_last_action?
         @updated_by_last_action
       end
   
       def updated?
         updated
       end
   
       class << self
   
         def json_create(o)
           resource = self.new(o["instance_vars"]["@name"])
           o["instance_vars"].each do |k,v|
             resource.instance_variable_set("@#{k}".to_sym, v)
           end
           resource
         end
   
         include Chef::Mixin::ConvertToClassName
   
         def attribute(attr_name, validation_opts={})
           # This atrocity is the only way to support 1.8 and 1.9 at the same time
           # When you're ready to drop 1.8 support, do this:
           # define_method attr_name.to_sym do |arg=nil|
           # etc.
           shim_method=<<-SHIM
           def #{attr_name}(arg=nil)
             _set_or_return_#{attr_name}(arg)
           end
           SHIM
           class_eval(shim_method)
   
           define_method("_set_or_return_#{attr_name.to_s}".to_sym) do |arg|
             set_or_return(attr_name.to_sym, arg, validation_opts)
           end
         end
         
         def build_from_file(cookbook_name, filename, run_context)
           rname = filename_to_qualified_string(cookbook_name, filename)
   
           # Add log entry if we override an existing light-weight resource.
           class_name = convert_to_class_name(rname)
           overriding = Chef::Resource.const_defined?(class_name)
           Chef::Log.info("#{class_name} light-weight resource already initialized -- overriding!") if overriding
   
           new_resource_class = Class.new self do |cls|
   
             # default initialize method that ensures that when initialize is finally
             # wrapped (see below), super is called in the event that the resource
             # definer does not implement initialize
             def initialize(name, run_context)
               super(name, run_context)
             end
   
             @actions_to_create = []
   
             class << cls
               include Chef::Mixin::FromFile
               
               attr_accessor :run_context
   
               def node
                 self.run_context.node
               end
   
               def actions_to_create
                 @actions_to_create
               end
   
               define_method(:actions) do |*action_names|
                 actions_to_create.push(*action_names)
               end
             end
   
             # set the run context in the class instance variable
             cls.run_context = run_context
             
             # load resource definition from file
             cls.class_from_file(filename)
   
             # create a new constructor that wraps the old one and adds the actions
             # specified in the DSL
             old_init = instance_method(:initialize)
   
             define_method(:initialize) do |name, *optional_args|
               args_run_context = optional_args.shift
               @resource_name = rname.to_sym
               old_init.bind(self).call(name, args_run_context)
               allowed_actions.push(self.class.actions_to_create).flatten!
             end
           end
   
           # register new class as a Chef::Resource
           class_name = convert_to_class_name(rname)
           Chef::Resource.const_set(class_name, new_resource_class)
           Chef::Log.debug("Loaded contents of #{filename} into a resource named #{rname} defined in Chef::Resource::#{class_name}")
   
           new_resource_class
         end
   
         # Resources that want providers namespaced somewhere other than
         # Chef::Provider can set the namespace with +provider_base+
         # Ex:
         #   class MyResource < Chef::Resource
         #     provider_base Chef::Provider::Deploy
         #     # ...other stuff
         #   end
         def provider_base(arg=nil)
           @provider_base ||= arg
           @provider_base ||= Chef::Provider
         end
   
       end
   
       private
   
       def lookup_provider_constant(name)
         begin
           self.class.provider_base.const_get(convert_to_class_name(name.to_s))
         rescue NameError => e
           if e.to_s =~ /#{Regexp.escape(self.class.provider_base.to_s)}/
             raise ArgumentError, "No provider found to match '#{name}'"
           else
             raise e
           end
         end
       end
   
     end
   end

lib/chef/cookbook/cookbook_version_loader.rb

   
   require 'chef/config'
   require 'chef/cookbook_version'
   require 'chef/cookbook/chefignore'
   require 'chef/cookbook/metadata'
   
   class Chef
     class Cookbook
       class CookbookVersionLoader
   
         FILETYPES_SUBJECT_TO_IGNORE = [ :attribute_filenames,
                                         :definition_filenames,
                                         :recipe_filenames,
                                         :template_filenames,
                                         :file_filenames,
                                         :library_filenames,
                                         :resource_filenames,
                                         :provider_filenames]
   
   
         attr_reader :cookbook_name
         attr_reader :cookbook_settings
         attr_reader :metadata_filenames
   
         def initialize(path, chefignore=nil)
           @cookbook_path = File.expand_path( path )
           @cookbook_name = File.basename( path )
           @chefignore = chefignore
           @metadata = Hash.new
           @relative_path = /#{Regexp.escape(@cookbook_path)}\/(.+)$/
           @cookbook_settings = {
             :attribute_filenames  => {},
             :definition_filenames => {},
             :recipe_filenames     => {},
             :template_filenames   => {},
             :file_filenames       => {},
             :library_filenames    => {},
             :resource_filenames   => {},
             :provider_filenames   => {},
             :root_filenames       => {}
           }
   
           @metadata_filenames = []
         end
   
         def load_cookbooks
           load_as(:attribute_filenames, 'attributes', '*.rb')
           load_as(:definition_filenames, 'definitions', '*.rb')
           load_as(:recipe_filenames, 'recipes', '*.rb')
           load_as(:library_filenames, 'libraries', '*.rb')
           load_recursively_as(:template_filenames, "templates", "*")
           load_recursively_as(:file_filenames, "files", "*")
           load_recursively_as(:resource_filenames, "resources", "*.rb")
           load_recursively_as(:provider_filenames, "providers", "*.rb")
           load_root_files
   
           remove_ignored_files
   
           if File.exists?(File.join(@cookbook_path, "metadata.rb"))
             @metadata_filenames << File.join(@cookbook_path, "metadata.rb")
           elsif File.exists?(File.join(@cookbook_path, "metadata.json"))
             @metadata_filenames << File.join(@cookbook_path, "metadata.json")
           end
   
           if empty?
             Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
           end
           @cookbook_settings
         end
   
         def cookbook_version
           return nil if empty?
   
           Chef::CookbookVersion.new(@cookbook_name.to_sym).tap do |c|
             c.root_dir             = @cookbook_path
             c.attribute_filenames  = cookbook_settings[:attribute_filenames].values
             c.definition_filenames = cookbook_settings[:definition_filenames].values
             c.recipe_filenames     = cookbook_settings[:recipe_filenames].values
             c.template_filenames   = cookbook_settings[:template_filenames].values
             c.file_filenames       = cookbook_settings[:file_filenames].values
             c.library_filenames    = cookbook_settings[:library_filenames].values
             c.resource_filenames   = cookbook_settings[:resource_filenames].values
             c.provider_filenames   = cookbook_settings[:provider_filenames].values
             c.root_filenames       = cookbook_settings[:root_filenames].values
             c.metadata_filenames   = @metadata_filenames
             c.metadata             = metadata(c)
           end
         end
   
         # Generates the Cookbook::Metadata object
         def metadata(cookbook_version)
           @metadata = Chef::Cookbook::Metadata.new(cookbook_version)
           @metadata_filenames.each do |metadata_file|
             case metadata_file
             when /\.rb$/
               apply_ruby_metadata(metadata_file)
             when /\.json$/
               apply_json_metadata(metadata_file)
             else
               raise RuntimeError, "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}"
             end
           end
           @metadata
         end
   
         def empty?
           cookbook_settings.inject(true) do |all_empty, files|
             all_empty && files.last.empty?
           end
         end
   
         def merge!(other_cookbook_loader)
           other_cookbook_settings = other_cookbook_loader.cookbook_settings
           @cookbook_settings.each do |file_type, file_list|
             file_list.merge!(other_cookbook_settings[file_type])
           end
           @metadata_filenames.concat(other_cookbook_loader.metadata_filenames)
         end
   
         def chefignore
           @chefignore ||= Chefignore.new(File.basename(@cookbook_path))
         end
   
         def load_root_files
           Dir.glob(File.join(@cookbook_path, '*'), File::FNM_DOTMATCH).each do |file|
             next if File.directory?(file)
             @cookbook_settings[:root_filenames][file[@relative_path, 1]] = file
           end
         end
   
         def load_recursively_as(category, category_dir, glob)
           file_spec = File.join(@cookbook_path, category_dir, '**', glob)
           Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file|
             next if File.directory?(file)
             @cookbook_settings[category][file[@relative_path, 1]] = file
           end
         end
   
         def load_as(category, *path_glob)
           Dir[File.join(@cookbook_path, *path_glob)].each do |file|
             @cookbook_settings[category][file[@relative_path, 1]] = file
           end
         end
   
         def remove_ignored_files
           @cookbook_settings.each_value do |file_list|
             file_list.reject! do |relative_path, full_path|
               chefignore.ignored?(relative_path)
             end
           end
         end
   
         def apply_ruby_metadata(file)
           begin
             @metadata.from_file(file)
           rescue JSON::ParserError
             Chef::Log.error("Error evaluating metadata.rb for #@cookbook_name in " + file)
             raise
           end
         end
   
         def apply_json_metadata(file)
           begin
             @metadata.from_json(IO.read(file))
           rescue JSON::ParserError
             Chef::Log.error("Couldn't parse cookbook metadata JSON for #@cookbook_name in " + file)
             raise
           end
         end
   
       end
     end
   end

lib/chef/provider/service/upstart.rb

   #
   # Author:: Bryan McLellan 
   # Copyright:: Copyright (c) 2010 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/simple'
   require 'chef/mixin/command'
   require 'chef/util/file_edit'
   
   class Chef
     class Provider
       class Service
         class Upstart < Chef::Provider::Service::Simple
           UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/
          
           # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in.
           # In chef, when we ask a service to start, we expect it to have started before performing the next step
           # since we have top down dependencies. Which is to say we may follow witha resource next that requires
           # that service to be running. According to [2] we can trust that sending a 'goal' such as start will not
           # return until that 'goal' is reached, or some error has occured.
           #
           # [1] http://upstart.ubuntu.com/wiki/JobStates
           # [2] http://www.netsplit.com/2008/04/27/upstart-05-events/
   
           def initialize(new_resource, run_context)
             # TODO: re-evaluate if this is needed after integrating cookbook fix
             raise ArgumentError, "run_context cannot be nil" unless run_context
             super
             
             run_context.node
             
             platform, version = Chef::Platform.find_platform_and_version(run_context.node)
             if platform == "ubuntu" && (8.04..9.04).include?(version.to_f)
               @upstart_job_dir = "/etc/event.d"
               @upstart_conf_suffix = ""
             else
               @upstart_job_dir = "/etc/init"
               @upstart_conf_suffix = ".conf"
             end
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::Service.new(@new_resource.name)
             @current_resource.service_name(@new_resource.service_name)
   
             # Get running/stopped state
             # We do not support searching for a service via ps when using upstart since status is a native
             # upstart function. We will however support status_command in case someone wants to do something special.
             if @new_resource.status_command
               Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
   
               begin
                 if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
                   @current_resource.running true
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
             else
               begin
                 if upstart_state == "running"
                   @current_resource.running true
                 else
                   @current_resource.running false
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
             end
   
             # Get enabled/disabled state by reading job configuration file
             if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
               Chef::Log.debug("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
               ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}",'r') do |file|
                 while line = file.gets
                   case line
                   when /^start on/
                     Chef::Log.debug("#{@new_resource} enabled: #{line.chomp}")
                     @current_resource.enabled true
                     break
                   when /^#start on/
                     Chef::Log.debug("#{@new_resource} disabled: #{line.chomp}")
                     @current_resource.enabled false
                     break
                   end
                 end
               end
             else
               Chef::Log.debug("#{@new_resource} did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
               @current_resource.enabled false
             end
   
             @current_resource
           end
   
           def start_service
             # Calling start on a service that is already started will return 1
             # Our 'goal' when we call start is to ensure the service is started
             if @current_resource.running
               Chef::Log.debug("#{@new_resource} already running, not starting")
             else
               if @new_resource.start_command
                 super
               else
                 run_command_with_systems_locale(:command => "/sbin/start #{@new_resource.service_name}")
               end
             end
           end
   
           def stop_service
             # Calling stop on a service that is already stopped will return 1
             # Our 'goal' when we call stop is to ensure the service is stopped
             unless @current_resource.running
               Chef::Log.debug("#{@new_resource} not running, not stopping")
             else
               if @new_resource.stop_command
                 super
               else
                 run_command_with_systems_locale(:command => "/sbin/stop #{@new_resource.service_name}")
               end
             end
           end
   
           def restart_service
             if @new_resource.restart_command
               super
             # Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start.
             # Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883
             else @new_resource.supports[:restart]
               if @current_resource.running
                 run_command_with_systems_locale(:command => "/sbin/restart #{@new_resource.service_name}")
               else
                 start_service
               end
             end
           end
   
           def reload_service
             if @new_resource.reload_command
               super
             else
               # upstart >= 0.6.3-4 supports reload (HUP)
               run_command_with_systems_locale(:command => "/sbin/reload #{@new_resource.service_name}")
             end
           end
   
           # https://bugs.launchpad.net/upstart/+bug/94065
   
           def enable_service
             Chef::Log.debug("#{@new_resource} upstart lacks inherent support for enabling services, editing job config file")
             conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
             conf.search_file_replace(/^#start on/, "start on")
             conf.write_file
           end
   
           def disable_service
             Chef::Log.debug("#{@new_resource} upstart lacks inherent support for disabling services, editing job config file")
             conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
             conf.search_file_replace(/^start on/, "#start on")
             conf.write_file
           end
   
           def upstart_state
             command = "/sbin/status #{@new_resource.service_name}"
             status = popen4(command) do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 # rsyslog stop/waiting
                 # service goal/state
                 # OR
                 # rsyslog (stop) waiting
                 # service (goal) state
                 line =~ UPSTART_STATE_FORMAT
                 data = Regexp.last_match
                 return data[2]
               end
             end
           end
   
         end
       end
     end
   end

lib/chef/node/attribute.rb

   #
   # Author:: Adam Jacob ()
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/deep_merge'
   require 'chef/log'
   
   class Chef
     class Node
       class Attribute
         HIDDEN_ATTRIBUES = [:@override, :@attribute, :@default, :@normal, :@automatic]
   
         attr_accessor :normal,
                       :default,
                       :override,
                       :automatic,
                       :current_normal,
                       :current_default,
                       :current_override,
                       :current_automatic,
                       :auto_vivifiy_on_read,
                       :set_unless_value_present,
                       :set_type
   
         include Enumerable
   
         def initialize(normal, default, override, automatic, state=[])
           @normal = normal
           @current_normal = normal
           @default = default
           @current_default = default
           @override = override
           @current_override = override
           @automatic = automatic
           @current_automatic = automatic
           @current_nesting_level = state
           @auto_vivifiy_on_read = false
           @set_unless_value_present = false
           @set_type = nil
           @has_been_read = false
         end
   
         def attribute
           normal
         end
   
         def attribute=(value)
           normal = value
         end
   
         def set_type_hash
           case @set_type
           when :normal
             @normal
           when :override
             @override
           when :default
             @default
           when :automatic
             @automatic
           end
         end
   
         # Reset our internal current_nesting_level to the top of every tree
         def reset
           @current_normal = @normal
           @current_default = @default
           @current_override = @override
           @current_automatic = @automatic
           @has_been_read = false
           @current_nesting_level = []
         end
   
         def [](key)
           @current_nesting_level << key
   
           # We set this to so that we can cope with ||= as a setting.
           # See the comments in []= for more details.
           @has_been_read = true
   
           # If we have a set type, our destiny is to write
           if @set_type
             a_value = @set_type == :automatic ? value_or_descend(current_automatic, key, auto_vivifiy_on_read) : nil
             o_value = @set_type == :override ? value_or_descend(current_override, key, auto_vivifiy_on_read) : nil
             n_value = @set_type == :normal ? value_or_descend(current_normal, key, auto_vivifiy_on_read) : nil
             d_value = @set_type == :default ? value_or_descend(current_default, key, auto_vivifiy_on_read) : nil
   
             determine_value(a_value, o_value, n_value, d_value)
           # Our destiny is only to read, so we get the full list.
           else
             a_value = value_or_descend(current_automatic, key)
             o_value = value_or_descend(current_override, key)
             n_value = value_or_descend(current_normal, key)
             d_value = value_or_descend(current_default, key)
   
             determine_value(a_value, o_value, n_value, d_value)
           end
         end
   
         def has_key?(key)
           return true if component_has_key?(@default,key)
           return true if component_has_key?(@automatic,key)
           return true if component_has_key?(@normal,key)
           return true if component_has_key?(@override,key)
           false
         end
   
         alias :attribute? :has_key?
         alias :include?   :has_key?
         alias :key?       :has_key?
         alias :member?    :has_key?
   
         def each(&block)
           get_keys.each do |key|
             value = determine_value(
               get_value(automatic, key),
               get_value(override, key),
               get_value(normal, key),
               get_value(default, key)
             )
             block.call([key, value])
           end
         end
   
         def each_pair(&block)
           get_keys.each do |key|
             value = determine_value(
               get_value(automatic, key),
               get_value(override, key),
               get_value(normal, key),
               get_value(default, key)
             )
             block.call(key, value)
           end
         end
   
         def each_attribute(&block)
           get_keys.each do |key|
             value = determine_value(
               get_value(automatic, key),
               get_value(override, key),
               get_value(normal, key),
               get_value(default, key)
             )
             block.call(key, value)
           end
         end
   
         def each_key(&block)
           get_keys.each do |key|
             block.call(key)
           end
         end
   
         def each_value(&block)
           get_keys.each do |key|
             value = determine_value(
               get_value(automatic, key),
               get_value(override, key),
               get_value(normal, key),
               get_value(default, key)
             )
             block.call(value)
           end
         end
   
         def empty?
           get_keys.empty?
         end
   
         def fetch(key, default_value=nil, &block)
           if get_keys.include? key
             determine_value(
               get_value(automatic, key),
               get_value(override, key),
               get_value(normal, key),
               get_value(default, key)
             )
           elsif default_value
             default_value
           elsif block_given?
             block.call(key)
           else
             raise IndexError, "Key #{key} does not exist"
           end
         end
   
         # Writing this method hurts me a little bit.
         #
         # TODO: Refactor all this stuff so this kind of horror is no longer needed
         #
         # We have invented a new kind of duck-typing, we call it Madoff typing.
         # We just lie and hope we die before you recognize our scheme. :)
         def kind_of?(klass)
           if klass == Hash || klass == Mash || klass == Chef::Node::Attribute
             true
           else
             false
           end
         end
   
         def has_value?(value)
           self.any? do |k,v|
             value == v
           end
         end
   
         alias :value? :has_value?
   
         def index(value)
           index = self.find do |h|
             value == h[1]
           end
           index.first if index.is_a? Array || nil
         end
   
         def values
           self.collect { |h| h[1] }
         end
   
         def size
           self.collect{}.length
         end
   
         alias :length :size
   
         def get_keys
           keys
         end
   
         def keys
           tkeys = current_automatic ? current_automatic.keys : []
           [ current_override, current_normal, current_default ].each do |attr_hash|
             if attr_hash
               attr_hash.keys.each do |key|
                 tkeys << key unless tkeys.include?(key)
               end
             end
           end
           tkeys
         end
   
         def get_value(data_hash, key)
           last = nil
   
           if @current_nesting_level.length == 0
             if data_hash.has_key?(key) && ! data_hash[key].nil?
               return data_hash[key]
             else
               return nil
             end
           end
   
           0.upto(@current_nesting_level.length) do |i|
             if i == 0
               last = auto_vivifiy(data_hash, @current_nesting_level[i])
             elsif i == @current_nesting_level.length
               fk = last[@current_nesting_level[i - 1]]
               if fk.has_key?(key) && ! fk[key].nil?
                 return fk[key]
               else
                 return nil
               end
             else
               last = auto_vivifiy(last[@current_nesting_level[i - 1]], @current_nesting_level[i])
             end
           end
         end
   
         def hash_and_not_cna?(to_check)
           (! to_check.kind_of?(Chef::Node::Attribute)) && to_check.respond_to?(:has_key?)
         end
   
         def determine_value(a_value, o_value, n_value, d_value)
           if hash_and_not_cna?(a_value)
             value = {}
             value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value)
             value = Chef::Mixin::DeepMerge.merge(value, n_value) if hash_and_not_cna?(n_value)
             value = Chef::Mixin::DeepMerge.merge(value, o_value) if hash_and_not_cna?(o_value)
             value = Chef::Mixin::DeepMerge.merge(value, a_value)
             value
           elsif hash_and_not_cna?(o_value)
             value = {}
             value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value)
             value = Chef::Mixin::DeepMerge.merge(value, n_value) if hash_and_not_cna?(n_value)
             value = Chef::Mixin::DeepMerge.merge(value, o_value)
             value
           elsif hash_and_not_cna?(n_value)
             value = {}
             value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value)
             value = Chef::Mixin::DeepMerge.merge(value, n_value)
             value
           elsif hash_and_not_cna?(d_value)
             d_value
           else
             return a_value if ! a_value.nil?
             return o_value if ! o_value.nil?
             return n_value if ! n_value.nil?
             return d_value if ! d_value.nil?
             return nil
           end
         end
   
         def []=(key, value)
           # If we don't have one, then we'll pretend we're normal
           @set_type ||= :normal
   
           if set_unless_value_present
             if get_value(set_type_hash, key) != nil
               Chef::Log.debug("Not setting #{@current_nesting_level.join("/")}/#{key} to #{value.inspect} because it has a #{@set_type} value already")
               return false
             end
           end
   
           # If we have been read, and the key we are writing is the same
           # as our parent, we have most like been ||='ed.  So we need to
           # just rewind a bit.
           #
           # In practice, these objects are single use - this is just
           # supporting one more single-use style.
           @current_nesting_level.pop if @has_been_read && @current_nesting_level.last == key
   
           set_value(set_type_hash, key, value)
           value
         end
   
         def set_value(data_hash, key, value)
           last = nil
   
           # If there is no current_nesting_level, just set the value
           if @current_nesting_level.length == 0
             data_hash[key] = value
             return data_hash
           end
   
           # Walk all the previous places we have been
           0.upto(@current_nesting_level.length) do |i|
             # If we are the first, we are top level, and should vivifiy the data_hash
             if i == 0
               last = auto_vivifiy(data_hash, @current_nesting_level[i])
             # If we are one past the last current_nesting_level, we are adding a key to that hash with a value
             elsif i == @current_nesting_level.length
               last[@current_nesting_level[i - 1]][key] = value
             # Otherwise, we're auto-vivifiy-ing an interim mash
             else
               last = auto_vivifiy(last[@current_nesting_level[i - 1]], @current_nesting_level[i])
             end
           end
           data_hash
         end
   
         def auto_vivifiy_on_read?
           auto_vivifiy_on_read
         end
   
         def auto_vivifiy(data_hash, key)
           if data_hash.has_key?(key)
             unless data_hash[key].respond_to?(:has_key?)
               raise ArgumentError, "You tried to set a nested key, where the parent is not a hash-like object: #{@current_nesting_level.join("/")}/#{key} " unless auto_vivifiy_on_read
             end
           else
             data_hash[key] = Mash.new
           end
           data_hash
         end
   
         def value_or_descend(data_hash, key, auto_vivifiy=false)
           if auto_vivifiy
             hash_to_vivifiy = auto_vivifiy(data_hash, key)
             data_hash[key] = hash_to_vivifiy[key]
           else
             return nil if data_hash == nil
             return nil unless data_hash.has_key?(key)
           end
   
           if data_hash[key].respond_to?(:has_key?)
             cna = Chef::Node::Attribute.new(@normal, @default, @override, @automatic, @current_nesting_level)
             cna.current_normal = current_normal.nil? ? Mash.new : current_normal[key]
             cna.current_default   = current_default.nil? ? Mash.new : current_default[key]
             cna.current_override  = current_override.nil? ? Mash.new : current_override[key]
             cna.current_automatic  = current_automatic.nil? ? Mash.new : current_automatic[key]
             cna.auto_vivifiy_on_read = auto_vivifiy_on_read
             cna.set_unless_value_present = set_unless_value_present
             cna.set_type = set_type
             cna
           else
             data_hash[key]
           end
         end
   
         # Fetches or sets the value, depending on if any arguments are given.
         # ==== Fetching
         # If no arguments are given, fetches the value:
         #   node.network
         #   => {network data}
         # Getters will find either a string or symbol key.
         # ==== Setting
         # If arguments are given, a value will be set. Both normal setter and DSL
         # style setters are allowed:
         #   node.foo = "bar"
         #   node.foo("bar")
         # Both set node[:foo] = "bar"
         def method_missing(symbol, *args)
           if args.empty?
             if key?(symbol)
               self[symbol]
             elsif key?(symbol.to_s)
               self[symbol.to_s]
             elsif auto_vivifiy_on_read?
               self[symbol] = Mash.new
               self[symbol]
             else
               raise ArgumentError, "Attribute #{symbol} is not defined!" unless auto_vivifiy_on_read
             end
           else
             key_to_set = symbol.to_s[/^(.+)=$/, 1] || symbol
             self[key_to_set] = (args.length == 1 ? args[0] : args)
           end
         end
   
         def inspect
           determine_value(current_automatic, current_override, current_normal, current_default)
   
           "#<#{self.class} " << instance_variables.map{|iv|
             iv.to_s + '=' + (HIDDEN_ATTRIBUES.include?(iv.to_sym) ? "{...}" : instance_variable_get(iv).inspect)
           }.join(', ') << ">"
         end
   
         def to_hash
           result = determine_value(current_automatic, current_override, current_normal, current_default)
           if result.class == Hash
             result
           else
             result.to_hash
           end
         end
   
         def delete(key)
           [@automatic, @override, @normal, @default].inject(nil) do |return_value, attrs|
             deleted_value = delete_from_component(attrs, key)
             return_value || deleted_value
           end
         end
   
         def delete_from_component(component_attrs, key)
           # get the Hash-like object at the current nesting level:
           nested_attrs = value_at_current_nesting(component_attrs, key)
   
           if nested_attrs.respond_to?(:delete)
             nested_attrs.delete(key)
           else
             nil
           end
         end
   
         def component_has_key?(component_attrs,key)
           # get the Hash-like object at the current nesting level:
           nested_attrs = value_at_current_nesting(component_attrs, key)
           nested_attrs.respond_to?(:key?) && nested_attrs.key?(key)
         end
   
         def value_at_current_nesting(component_attrs, key)
           @current_nesting_level.inject(component_attrs) do |subtree, intermediate_key|
             # if the intermediate value isn't a hash or doesn't have the intermediate key,
             # it can't have the bottom-level key we're looking for.
             (subtree.respond_to?(:key?) && subtree[intermediate_key]) or (return false)
           end
         end
   
       end
     end
   end

lib/chef/provider/service/simple.rb

   #
   # Author:: Mathieu Sauve-Frankel 
   # Copyright:: Copyright (c) 2009 Mathieu Sauve-Frankel
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Simple < Chef::Provider::Service
           def load_current_resource
             @current_resource = Chef::Resource::Service.new(@new_resource.name)
             @current_resource.service_name(@new_resource.service_name)
             if @new_resource.status_command
               Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
   
               begin
                 if run_command(:command => @new_resource.status_command) == 0
                   @current_resource.running true
                   Chef::Log.debug("#{@new_resource} is running")
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
   
             elsif @new_resource.supports[:status]
               Chef::Log.debug("#{@new_resource} supports status, running")
   
               begin
                 if run_command(:command => "#{@init_command} status") == 0
                   @current_resource.running true
                   Chef::Log.debug("#{@new_resource} is running")
                 end
               rescue Chef::Exceptions::Exec
                 @current_resource.running false
                 nil
               end
             elsif
               Chef::Log.debug "#{@new_resource} falling back to process table inspection"
               if ps_cmd.nil? or ps_cmd.empty?
                 raise Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this nodes 'command.ps' attribute"
               end
               status = popen4(ps_cmd) do |pid, stdin, stdout, stderr|
                 r = Regexp.new(@new_resource.pattern)
                 Chef::Log.debug "#{@new_resource} attempting to match '#{@new_resource.pattern}' (#{r.inspect}) against process list"
                 stdout.each_line do |line|
                   if r.match(line)
                     @current_resource.running true
                     break
                   end
                 end
                 @current_resource.running false unless @current_resource.running
               end
               unless status.exitstatus == 0
                 raise Chef::Exceptions::Service, "Command #{ps_cmd} failed"
               else
                 Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
               end
             end
   
             @current_resource
           end
   
           def start_service
             if @new_resource.start_command
               run_command(:command => @new_resource.start_command)
             else
               raise Chef::Exceptions::Service, "#{self.to_s} requires that start_command to be set"
             end
           end
   
           def stop_service
             if @new_resource.stop_command
               run_command(:command => @new_resource.stop_command)
             else
               raise Chef::Exceptions::Service, "#{self.to_s} requires that stop_command to be set"
             end
           end
   
           def restart_service
             if @new_resource.restart_command
               run_command(:command => @new_resource.restart_command)
             else
               stop_service
               sleep 1
               start_service
             end
           end
   
           def reload_service
             if @new_resource.reload_command
               run_command(:command => @new_resource.reload_command)
             else
               raise Chef::Exceptions::Service, "#{self.to_s} requires that reload_command to be set"
             end
           end
   
           def ps_cmd
             @run_context.node[:command] && @run_context.node[:command][:ps]
           end
         end
       end
     end
   end
   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/log'
   require 'chef/mixin/shell_out'
   require 'chef/resource/link'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Link < Chef::Provider
         include Chef::Mixin::ShellOut
         #include Chef::Mixin::Command
   
         def negative_complement(big)
           if big > 1073741823 # Fixnum max
             big -= (2**32) # diminished radix wrap to negative
           end
           big
         end
   
         private :negative_complement
   
         def load_current_resource
           @current_resource = Chef::Resource::Link.new(@new_resource.name)
           @current_resource.target_file(@new_resource.target_file)
           @current_resource.link_type(@new_resource.link_type)
           if @new_resource.link_type == :symbolic
             if ::File.exists?(@current_resource.target_file) && ::File.symlink?(@current_resource.target_file)
               @current_resource.to(
                 ::File.expand_path(::File.readlink(@current_resource.target_file))
               )
               cstats = ::File.lstat(@current_resource.target_file)
               @current_resource.owner(cstats.uid)
               @current_resource.group(cstats.gid)
             else
               @current_resource.to("")
             end
           elsif @new_resource.link_type == :hard
             if ::File.exists?(@current_resource.target_file) && ::File.exists?(@new_resource.to)
               if ::File.stat(@current_resource.target_file).ino == ::File.stat(@new_resource.to).ino
                 @current_resource.to(@new_resource.to)
               else
                 @current_resource.to("")
               end
             else
               @current_resource.to("")
             end
           end
           @current_resource
         end
   
         # Compare the ownership of a symlink.  Returns true if they are the same, false if they are not.
         def compare_owner
           return false if @new_resource.owner.nil?
   
           @set_user_id = case @new_resource.owner
                          when /^\d+$/, Integer
                            @new_resource.owner.to_i
                          else
                            # This raises an ArgumentError if you can't find the user
                            Etc.getpwnam(@new_resource.owner).uid
                          end
   
           @set_user_id == @current_resource.owner
         end
   
         # Set the ownership on the symlink, assuming it is not set correctly already.
         def set_owner
           unless compare_owner
             @set_user_id = negative_complement(@set_user_id)
             ::File.lchown(@set_user_id, nil, @new_resource.target_file)
             Chef::Log.info("#{@new_resource} owner changed to #{@set_user_id}")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         # Compares the group of a symlink.  Returns true if they are the same, false if they are not.
         def compare_group
           return false if @new_resource.group.nil?
   
           @set_group_id = case @new_resource.group
                           when /^\d+$/, Integer
                             @new_resource.group.to_i
                           else
                             Etc.getgrnam(@new_resource.group).gid
                           end
   
           @set_group_id == @current_resource.group
         end
   
         def set_group
           unless compare_group
             @set_group_id = negative_complement(@set_group_id)
             ::File.lchown(nil, @set_group_id, @new_resource.target_file)
             Chef::Log.info("#{@new_resource} group changed to #{@set_group_id}")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def action_create
           if @current_resource.to != ::File.expand_path(@new_resource.to, @new_resource.target_file)
             if @new_resource.link_type == :symbolic
               unless (::File.symlink?(@new_resource.target_file) && ::File.readlink(@new_resource.target_file) == @new_resource.to)
                 if ::File.symlink?(@new_resource.target_file) || ::File.exist?(@new_resource.target_file)
                   ::File.unlink(@new_resource.target_file)
                 end
                 ::File.symlink(@new_resource.to,@new_resource.target_file)
                 Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.to} -> #{@new_resource.target_file}")
                 Chef::Log.info("#{@new_resource} created")
               end
             elsif @new_resource.link_type == :hard
               ::File.link(@new_resource.to, @new_resource.target_file)
               Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.to} -> #{@new_resource.target_file}")
               Chef::Log.info("#{@new_resource} created")
             end
             @new_resource.updated_by_last_action(true)
           end
           if @new_resource.link_type == :symbolic
             set_owner unless @new_resource.owner.nil?
             set_group unless @new_resource.group.nil?
           end
         end
   
         def action_delete
           if @new_resource.link_type == :symbolic
             if ::File.symlink?(@new_resource.target_file)
               ::File.delete(@new_resource.target_file)
               Chef::Log.info("#{@new_resource} deleted")
               @new_resource.updated_by_last_action(true)
             elsif ::File.exists?(@new_resource.target_file)
               raise Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a symbolic link."
             end
           elsif @new_resource.link_type == :hard
             if ::File.exists?(@new_resource.target_file)
                if ::File.exists?(@new_resource.to) && ::File.stat(@current_resource.target_file).ino == ::File.stat(@new_resource.to).ino
                  ::File.delete(@new_resource.target_file)
                  Chef::Log.info("#{@new_resource} deleted")
                  @new_resource.updated_by_last_action(true)
                else
                  raise Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a hard link."
                end
             end
           end
         end
       end
     end
   end

/home/pratima/.chef/plugins/knife/envstatus.rb

   require 'chef/knife'
    
   module EnvironmentStatus
       class Envstatus < Chef::Knife
    		
   	deps do
   		require 'highline'
         		require 'chef/search/query'
         		require 'chef/knife/search'
       	end
    
       	banner "knife envstatus ENVIRONMENT"
    
       	def run
         	unless @env_name = name_args.first
           	ui.error "Please specify ENVIRONMENT"
           exit 1
         	end
   	
   	puts "Status of #{@env_name} environment"
   	nodes = Chef::Node.list_by_environment(@env_name)
   	nodes.each do |node|
   		puts node[0]
     	end
   	
   	@environment = Chef::Environment.load(@env_name)
           output(format_for_display(@environment))
       	
   	::Chef::Knife.run(['status', "chef_environment:#{@env_name}"])
   
   	end
       end
   end

lib/chef/cookbook_uploader.rb

   
   require 'set'
   require 'rest_client'
   require 'chef/exceptions'
   require 'chef/knife/cookbook_metadata'
   require 'chef/checksum_cache'
   require 'chef/sandbox'
   require 'chef/cookbook_version'
   require 'chef/cookbook/syntax_check'
   require 'chef/cookbook/file_system_file_vendor'
   
   class Chef
     class CookbookUploader
   
       def self.work_queue
         @work_queue ||= Queue.new
       end
   
       def self.setup_worker_threads
         @worker_threads ||= begin
           work_queue
           (1...10).map do
             Thread.new do
               loop do
                 work_queue.pop.call
               end
             end
           end
         end
       end
   
       attr_reader :cookbook
       attr_reader :path
       attr_reader :opts
       attr_reader :rest
   
       # Creates a new CookbookUploader.
       # ===Arguments:
       # * cookbook::: A Chef::CookbookVersion describing the cookbook to be uploaded
       # * path::: A String or Array of Strings representing the base paths to the
       #           cookbook repositories.
       # * opts::: (optional) An options Hash
       # ===Options:
       # * :force  indicates that the uploader should set the force option when
       #           uploading the cookbook. This allows frozen CookbookVersion
       #           documents on the server to be overwritten (otherwise a 409 is
       #           returned by the server)
       # * :rest   A Chef::REST object that you have configured the way you like it.
       #           If you don't provide this, one will be created using the values
       #           in Chef::Config.
       def initialize(cookbook, path, opts={})
         @cookbook, @path, @opts = cookbook, path, opts
         @rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def upload_cookbook
         Thread.abort_on_exception = true
         Chef::Log.info("Saving #{cookbook.name}")
   
         # Syntax Check
         validate_cookbook
         # generate checksums of cookbook files and create a sandbox
         checksum_files = cookbook.checksums
         checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo}
         new_sandbox = rest.post_rest("sandboxes", { :checksums => checksums })
   
         Chef::Log.info("Uploading files")
   
         self.class.setup_worker_threads
   
         checksums_to_upload = Set.new
   
         # upload the new checksums and commit the sandbox
         new_sandbox['checksums'].each do |checksum, info|
           if info['needs_upload'] == true
             checksums_to_upload << checksum
             Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}")
             self.class.work_queue << uploader_function_for(checksum_files[checksum], checksum, info['url'], checksums_to_upload)
           else
             Chef::Log.debug("#{checksum_files[checksum]} has not changed")
           end
         end
   
         until checksums_to_upload.empty?
           sleep 0.1
         end
   
         sandbox_url = new_sandbox['uri']
         Chef::Log.debug("Committing sandbox")
         # Retry if S3 is claims a checksum doesn't exist (the eventual
         # in eventual consistency)
         retries = 0
         begin
           rest.put_rest(sandbox_url, {:is_completed => true})
         rescue Net::HTTPServerException => e
           if e.message =~ /^400/ && (retries += 1) <= 5
             sleep 2
             retry
           else
             raise
           end
         end
   
         # files are uploaded, so save the manifest
         save_url = opts[:force] ? cookbook.force_save_url : cookbook.save_url
         rest.put_rest(save_url, cookbook)
   
         Chef::Log.info("Upload complete!")
       end
   
       def worker_thread(work_queue)
       end
   
       def uploader_function_for(file, checksum, url, checksums_to_upload)
         lambda do
           # Checksum is the hexadecimal representation of the md5,
           # but we need the base64 encoding for the content-md5
           # header
           checksum64 = Base64.encode64([checksum].pack("H*")).strip
           timestamp = Time.now.utc.iso8601
           file_contents = File.open(file, "rb") {|f| f.read}
           # TODO - 5/28/2010, cw: make signing and sending the request streaming
           sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
                                                                              :http_method => :put,
                                                                              :path        => URI.parse(url).path,
                                                                              :body        => file_contents,
                                                                              :timestamp   => timestamp,
                                                                              :user_id     => rest.client_name
                                                                              )
           headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, :accept => 'application/json' }
           headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key)))
   
           begin
             RestClient::Resource.new(url, :headers=>headers, :timeout=>1800, :open_timeout=>1800).put(file_contents)
             checksums_to_upload.delete(checksum)
           rescue RestClient::Exception => e
             Chef::Knife.ui.error("Failed to upload #@cookbook : #{e.message}\n#{e.response.body}")
             raise
           end
         end
       end
   
       def validate_cookbook
         syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook.name, @user_cookbook_path)
         Chef::Log.info("Validating ruby files")
         exit(1) unless syntax_checker.validate_ruby_files
         Chef::Log.info("Validating templates")
         exit(1) unless syntax_checker.validate_templates
         Chef::Log.info("Syntax OK")
         true
       end
   
     end
   end

lib/chef/provider/service/debian.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/invokercd'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Debian < Chef::Provider::Service::Invokercd
           UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
           UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
   
           def load_current_resource
             super
   
             @current_resource.priority(get_priority)
             @current_resource.enabled(service_currently_enabled?(@current_resource.priority))
             @current_resource
           end
   
           def assert_update_rcd_available
             unless ::File.exists? "/usr/sbin/update-rc.d"
               raise Chef::Exceptions::Service, "/usr/sbin/update-rc.d does not exist!"
             end
           end
   
           def get_priority
             assert_update_rcd_available
             priority = {}
   
             status = popen4("/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} remove") do |pid, stdin, stdout, stderr|
   
               [stdout, stderr].each do |iop|
                 iop.each_line do |line|
                   if UPDATE_RC_D_PRIORITIES =~ line
                     # priority[runlevel] = [ S|K, priority ] 
                     # S = Start, K = Kill
                     # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot
                     priority[$1] = [($2 == "S" ? :start : :stop), $3]
                   end
                   if line =~ UPDATE_RC_D_ENABLED_MATCHES
                     enabled = true
                   end
                 end
               end
             end
   
             unless status.exitstatus == 0
               raise Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{@current_resource.service_name} failed - #{status.inspect}"
             end
             priority
           end
   
           def service_currently_enabled?(priority)
             enabled = false
   
             priority.each { |runlevel, arguments|
               Chef::Log.debug("#{@new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
               
               # if we are in a update-rc.d default startup runlevel && we start in this runlevel
               if (2..5).include?(runlevel.to_i) && arguments[0] == :start
                 enabled = true
               end
             }
   
             enabled
           end
   
           def enable_service()
             if @new_resource.priority.is_a? Integer
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
               run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults #{@new_resource.priority} #{100 - @new_resource.priority}")
             elsif @new_resource.priority.is_a? Hash
               # we call the same command regardless of we're enabling or disabling  
               # users passing a Hash are responsible for setting their own start priorities
               set_priority()
             else # No priority, go with update-rc.d defaults
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
               run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} defaults")
             end
   
           end
   
           def disable_service()
             if @new_resource.priority.is_a? Integer
               # Stop processes in reverse order of start using '100 - start_priority'
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop #{100 - @new_resource.priority} 2 3 4 5 .")
             elsif @new_resource.priority.is_a? Hash
               # we call the same command regardless of we're enabling or disabling  
               # users passing a Hash are responsible for setting their own stop priorities
               set_priority()
             else 
               # no priority, using '100 - 20 (update-rc.d default)' to stop in reverse order of start
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
               run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} stop 80 2 3 4 5 .")
             end
           end
   
           def set_priority()
             args = ""
             @new_resource.priority.each do |level, o|
               action = o[0]
               priority = o[1]
               args += "#{action} #{priority} #{level} . "
             end
             run_command(:command => "/usr/sbin/update-rc.d -f #{@new_resource.service_name} remove")
             run_command(:command => "/usr/sbin/update-rc.d #{@new_resource.service_name} #{args}")
           end
         end
       end
     end
   end

lib/chef/provider/package/portage.rb

   #
   # Author:: Ezra Zygmuntowicz ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   
   class Chef
     class Provider
       class Package
         class Portage < Chef::Provider::Package
           PACKAGE_NAME_PATTERN = %r{(([^/]+)/)?([^/]+)}
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
   
             @current_resource.version(nil)
   
             _, category_with_slash, category, pkg = %r{^#{PACKAGE_NAME_PATTERN}$}.match(@new_resource.package_name).to_a
   
             possibilities = Dir["/var/db/pkg/#{category || "*"}/#{pkg}-*"].map {|d| d.sub(%r{/var/db/pkg/}, "") }
             versions = possibilities.map do |entry|
               if(entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)})
                 [$&, $1]
               end
             end.compact
   
             if versions.size > 1
               atoms = versions.map {|v| v.first }.sort
               raise Chef::Exceptions::Package, "Multiple packages found for #{@new_resource.package_name}: #{atoms.join(" ")}. Specify a category."
             elsif versions.size == 1
               @current_resource.version(versions.first.last)
               Chef::Log.debug("#{@new_resource} current version #{$1}")
             end
   
             @current_resource
           end
   
   
           def parse_emerge(package, txt)
             availables = {}
             package_without_category = package.split("/").last
             found_package_name = nil
   
             txt.each_line do |line|
               if line =~ /\*\s+#{PACKAGE_NAME_PATTERN}/
                 found_package_name = $&.strip
                 if found_package_name == package || found_package_name.split("/").last == package_without_category
                   availables[found_package_name] = nil
                 end
               end
   
               if line =~ /Latest version available: (.*)/ && availables.has_key?(found_package_name)
                 availables[found_package_name] = $1.strip
               end
             end
   
             if availables.size > 1
               # shouldn't happen if a category is specified so just use `package`
               raise Chef::Exceptions::Package, "Multiple emerge results found for #{package}: #{availables.keys.join(" ")}. Specify a category."
             end
   
             availables.values.first
           end
   
           def candidate_version
             return @candidate_version if @candidate_version
   
             status = popen4("emerge --color n --nospinner --search #{@new_resource.package_name.split('/').last}") do |pid, stdin, stdout, stderr|
               available, installed = parse_emerge(@new_resource.package_name, stdout.read)
               @candidate_version = available
             end
   
             unless status.exitstatus == 0
               raise Chef::Exceptions::Package, "emerge --search failed - #{status.inspect}!"
             end
   
             @candidate_version
   
           end
   
   
           def install_package(name, version)
             pkg = "=#{name}-#{version}"
   
             if(version =~ /^\~(.+)/)
               # If we start with a tilde
               pkg = "~#{name}-#{$1}"
             end
   
             run_command_with_systems_locale(
               :command => "emerge -g --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}"
             )
           end
   
           def upgrade_package(name, version)
             install_package(name, version)
           end
   
           def remove_package(name, version)
             if(version)
               pkg = "=#{@new_resource.package_name}-#{version}"
             else
               pkg = "#{@new_resource.package_name}"
             end
   
             run_command_with_systems_locale(
               :command => "emerge --unmerge --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}"
             )
           end
   
           def purge_package(name, version)
             remove_package(name, version)
           end
   
         end
       end
     end
   end

lib/chef/provider/package/solaris.rb

   #
   # Author:: Toomas Pelberg ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'chef/mixin/get_source_from_package'
   
   class Chef
     class Provider
       class Package
         class Solaris < Chef::Provider::Package
   
           include Chef::Mixin::GetSourceFromPackage
   
           # def initialize(*args)
           #   super
           #   @current_resource = Chef::Resource::Package.new(@new_resource.name)
           # end
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             @new_resource.version(nil)
   
             if @new_resource.source
               unless ::File.exists?(@new_resource.source)
                 raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
               end
   
               Chef::Log.debug("#{@new_resource} checking pkg status")
               status = popen4("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
                 stdout.each do |line|
                   case line
                   when /VERSION:\s+(.+)/
                     @new_resource.version($1)
                   end
                 end
               end
             elsif Array(@new_resource.action).include?(:install)
               raise Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
             end
   
             Chef::Log.debug("#{@new_resource} checking install state")
             status = popen4("pkginfo -l #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                 when /VERSION:\s+(.+)/
                   Chef::Log.debug("#{@new_resource} version #{$1} is already installed")
                   @current_resource.version($1)
                 end
               end
             end
   
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "pkginfo failed - #{status.inspect}!"
             end
   
             unless @current_resource.version.nil?
               @current_resource.version(nil)
             end
   
             @current_resource
           end
   
           def candidate_version
             return @candidate_version if @candidate_version
             status = popen4("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line
                 when /VERSION:\s+(.+)/
                   @candidate_version = $1
                   @new_resource.version($1)
                   Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}")
                 end
               end
             end
             unless status.exitstatus == 0
               raise Chef::Exceptions::Package, "pkginfo -l -d #{@new_resource.source} - #{status.inspect}!"
             end
             @candidate_version
           end
   
           def install_package(name, version)
             Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}")
             if @new_resource.options.nil?
               run_command_with_systems_locale(
                       :command => "pkgadd -n -d #{@new_resource.source} all"
                     )
               Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
             else
               run_command_with_systems_locale(
                 :command => "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} all"
               )
               Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
             end
           end
   
           def remove_package(name, version)
             if @new_resource.options.nil?
               run_command_with_systems_locale(
                       :command => "pkgrm -n #{name}"
                     )
               Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
             else
               run_command_with_systems_locale(
                 :command => "pkgrm -n#{expand_options(@new_resource.options)} #{name}"
               )
               Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
             end
           end
   
         end
       end
     end
   end

lib/chef/knife/help.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Knife
       class Help < Chef::Knife
   
         banner "knife help [list|TOPIC]"
   
         def run
           if name_args.empty?
             ui.info "Usage: knife SUBCOMMAND (options)"
             ui.msg ""
             # This command is atypical, the user is likely not interested in usage of
             # this command, but knife in general. So hack the banner.
             opt_parser.banner = "General Knife Options:"
             ui.msg opt_parser.to_s
             ui.msg ""
             ui.info "For further help:"
             ui.info(<<-MOAR_HELP)
     knife help list             list help topics
     knife help knife            show general knife help
     knife help TOPIC            display the manual for TOPIC
     knife SUBCOMMAND --help     show the options for a command
   MOAR_HELP
             exit 1
           else
             @query = name_args.join('-')
           end
   
   
   
           case @query
           when 'topics', 'list'
             print_help_topics
             exit 1
           when 'intro', 'knife'
             @topic = 'knife'
           else
             @topic = find_manpages_for_query(@query)
           end
   
           manpage_path = find_manpage_path(@topic)
           exec "man #{manpage_path}"
         end
   
         def help_topics
           # The list of help topics is generated by a rake task from the available man pages
           # This constant is provided in help_topics.rb which is automatically required/loaded by the knife subcommand loader.
           HELP_TOPICS
         end
   
         def print_help_topics
           ui.info "Available help topics are: "
           help_topics.collect {|t| t.gsub(/knife-/, '') }.sort.each do |topic|
             ui.msg "  #{topic}"
           end
         end
   
         def find_manpages_for_query(query)
           possibilities = help_topics.select do |manpage|
             ::File.fnmatch("knife-#{query}*", manpage) || ::File.fnmatch("#{query}*", manpage)
           end
           if possibilities.empty?
             ui.error "No help found for '#{query}'"
             ui.msg ""
             print_help_topics
             exit 1
           elsif possibilities.size == 1
             possibilities.first
           else
             ui.info "Multiple help topics match your query. Pick one:"
             ui.highline.choose(*possibilities)
           end
         end
   
         def find_manpage_path(topic)
           if ::File.exists?(::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT))
             # If we've provided the man page in the gem, give that
             return ::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT)
           else
             # Otherwise, we'll just be using MANPATH
             topic
           end
         end
       end
     end
   end

lib/chef/provider/erl_call.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   
   class Chef
     class Provider
       class ErlCall < Chef::Provider
         include Chef::Mixin::Command
   
         def initialize(node, new_resource)
           super(node, new_resource)
         end
   
         def load_current_resource
           true
         end
   
         def action_run
           case @new_resource.name_type
           when "sname"
             node = "-sname #{@new_resource.node_name}"
           when "name"
             node = "-name #{@new_resource.node_name}"
           end
   
           if @new_resource.cookie
             cookie = "-c #{@new_resource.cookie}"
           else
             cookie = ""
           end
   
           if @new_resource.distributed
             distributed = "-s"
           else
             distributed = ""
           end
   
           command = "erl_call -e #{distributed} #{node} #{cookie}"
   
           begin
             pid, stdin, stdout, stderr = popen4(command, :waitlast => true)
   
             Chef::Log.debug("#{@new_resource} running")
             Chef::Log.debug("#{@new_resource} command: #{command}")
             Chef::Log.debug("#{@new_resource} code: #{@new_resource.code}")
   
             @new_resource.code.each_line { |line| stdin.puts(line.chomp) }
   
             stdin.close
   
             Chef::Log.debug("#{@new_resource} output: ")
   
             stdout_output = ""
             stdout.each_line { |line| stdout_output << line }
             stdout.close
   
             stderr_output = ""
             stderr.each_line { |line| stderr_output << line }
             stderr.close
   
             # fail if stderr contains anything
             if stderr_output.length > 0
               raise Chef::Exceptions::ErlCall, stderr_output
             end
   
             # fail if the first 4 characters aren't "{ok,"
             unless stdout_output[0..3].include?('{ok,')
               raise Chef::Exceptions::ErlCall, stdout_output
             end
   
             @new_resource.updated_by_last_action(true)
   
             Chef::Log.debug("#{@new_resource} #{stdout_output}")
             Chef::Log.info("#{@new_resouce} ran successfully")
           ensure
             Process.wait(pid) if pid
           end
   
         end
   
       end
     end
   end

lib/chef/provider/package/apt.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'chef/mixin/shell_out'
   
   
   class Chef
     class Provider
       class Package
         class Apt < Chef::Provider::Package
   
           include Chef::Mixin::ShellOut
           attr_accessor :is_virtual_package
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             check_package_state(@new_resource.package_name)
             @current_resource
           end
   
           def check_package_state(package)
             Chef::Log.debug("#{@new_resource} checking package status for #{package}")
             installed = false
   
             shell_out!("apt-cache policy #{package}").stdout.each_line do |line|
               case line
               when /^\s{2}Installed: (.+)$/
                 installed_version = $1
                 if installed_version == '(none)'
                   Chef::Log.debug("#{@new_resource} current version is nil")
                   @current_resource.version(nil)
                 else
                   Chef::Log.debug("#{@new_resource} current version is #{installed_version}")
                   @current_resource.version(installed_version)
                   installed = true
                 end
               when /^\s{2}Candidate: (.+)$/
                 candidate_version = $1
                 if candidate_version == '(none)'
                   # This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm
                   @is_virtual_package = true
                   showpkg = shell_out!("apt-cache showpkg #{package}").stdout
                   providers = Hash.new
                   showpkg.rpartition(/Reverse Provides:? #{$/}/)[2].each_line do |line|
                     provider, version = line.split
                     providers[provider] = version
                   end
                   # Check if the package providing this virtual package is installed
                   num_providers = providers.length
                   raise Chef::Exceptions::Package, "#{@new_resource.package_name} has no candidate in the apt-cache" if num_providers == 0
                   # apt will only install a virtual package if there is a single providing package
                   raise Chef::Exceptions::Package, "#{@new_resource.package_name} is a virtual package provided by #{num_providers} packages, you must explicitly select one to install" if num_providers > 1
                   # Check if the package providing this virtual package is installed
                   Chef::Log.info("#{@new_resource} is a virtual package, actually acting on package[#{providers.keys.first}]")
                   installed = check_package_state(providers.keys.first)
                 else
                   Chef::Log.debug("#{@new_resource} candidate version is #{$1}")
                   @candidate_version = $1
                 end
               end
             end
   
             return installed
           end
   
           def install_package(name, version)
             package_name = "#{name}=#{version}"
             package_name = name if @is_virtual_package
             run_command_with_systems_locale(
               :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{package_name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
   
           def upgrade_package(name, version)
             install_package(name, version)
           end
   
           def remove_package(name, version)
             package_name = "#{name}"
             run_command_with_systems_locale(
               :command => "apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
   
           def purge_package(name, version)
             run_command_with_systems_locale(
               :command => "apt-get -q -y#{expand_options(@new_resource.options)} purge #{@new_resource.package_name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
   
           def preseed_package(name, version)
             preseed_file = get_preseed_file(name, version)
             if preseed_file
               Chef::Log.info("#{@new_resource} pre-seeding package installation instructions")
               run_command_with_systems_locale(
                 :command => "debconf-set-selections #{preseed_file}",
                 :environment => {
                   "DEBIAN_FRONTEND" => "noninteractive"
                 }
               )
             end
           end
   
           def reconfig_package(name, version)
             Chef::Log.info("#{@new_resource} reconfiguring")
             run_command_with_systems_locale(
               :command => "dpkg-reconfigure #{name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
   
         end
       end
     end
   end

lib/chef/provider/package.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/command'
   require 'chef/log'
   require 'chef/file_cache'
   require 'chef/resource/remote_file'
   require 'chef/platform'
   
   class Chef
     class Provider
       class Package < Chef::Provider
   
         include Chef::Mixin::Command
   
         attr_accessor :candidate_version
   
         def initialize(new_resource, run_context)
           super
           @candidate_version = nil
         end
   
         def action_install
           # If we specified a version, and it's not the current version, move to the specified version
           if @new_resource.version != nil && @new_resource.version != @current_resource.version
             install_version = @new_resource.version
           # If it's not installed at all, install it
           elsif @current_resource.version == nil
             install_version = candidate_version
           else
             Chef::Log.debug("#{@new_resource} is already installed - nothing to do")
             return
           end
   
           unless install_version
             raise(Chef::Exceptions::Package, "No version specified, and no candidate version available for #{@new_resource.package_name}")
           end
   
   
           # We need to make sure we handle the preseed file
           if @new_resource.response_file
             preseed_package(@new_resource.package_name, install_version)
           end
   
           status = install_package(@new_resource.package_name, install_version)
           if status
             @new_resource.updated_by_last_action(true)
           end
           Chef::Log.info("#{@new_resource} installed version #{install_version}")
         end
   
         def action_upgrade
           if @current_resource.version != candidate_version
             orig_version = @current_resource.version || "uninstalled"
             status = upgrade_package(@new_resource.package_name, candidate_version)
             if status
               @new_resource.updated_by_last_action(true)
             end
             Chef::Log.info("#{@new_resource} upgraded from #{orig_version} to #{candidate_version}")
           else
             Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do")
           end
         end
   
         def action_remove
           if removing_package?
             remove_package(@current_resource.package_name, @new_resource.version)
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} removed")
           else
             Chef::Log.debug("#{@new_resource} package does not exist - nothing to do")
           end
         end
   
         def removing_package?
           if @current_resource.version.nil?
             false # nothing to remove
           elsif @new_resource.version.nil?
             true # remove any version of a package
           elsif @new_resource.version == @current_resource.version
             true # remove the version we have
           else
             false # we don't have the version we want to remove
           end
         end
   
         def action_purge
           if removing_package?
             purge_package(@current_resource.package_name, @new_resource.version)
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} purged")
           end
         end
   
         def action_reconfig
           if @current_resource.version == nil then
             Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
             return
           end
   
           unless @new_resource.response_file then
             Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do")
             return
           end
   
           status = preseed_package(@new_resource.package_name, @current_resource.version)
           unless status then
             Chef::Log.debug("#{@new_resource} preseeding has not changed - nothing to do")
             return
           end
   
           status = reconfig_package(@new_resource.package_name, @current_resource.version)
           @new_resource.updated_by_last_action(true) if status
           Chef::Log.info("#{@new_resource} reconfigured")
         end
   
         def install_package(name, version)
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :install"
         end
   
         def upgrade_package(name, version)
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :upgrade"
         end
   
         def remove_package(name, version)
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remove"
         end
   
         def purge_package(name, version)
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :purge"
         end
   
         def preseed_package(name, version)
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support pre-seeding package install/upgrade instructions - don't ask it to!"
         end
   
         def reconfig_package(name, version)
           raise( Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reconfig" )
         end
   
         def get_preseed_file(name, version)
           resource = preseed_resource(name, version)
           resource.run_action('create')
           Chef::Log.debug("#{@new_resource} fetched preseed file to #{resource.path}")
   
           if resource.updated_by_last_action?
             resource.path
           else
             false
           end
         end
   
         def preseed_resource(name, version)
           # A directory in our cache to store this cookbook's preseed files in
           file_cache_dir = Chef::FileCache.create_cache_path("preseed/#{@new_resource.cookbook_name}")
           # The full path where the preseed file will be stored
           cache_seed_to = "#{file_cache_dir}/#{name}-#{version}.seed"
   
           Chef::Log.debug("#{@new_resource} fetching preseed file to #{cache_seed_to}")
   
           begin
             remote_file = Chef::Resource::Template.new(cache_seed_to, run_context)
             remote_file.cookbook_name = @new_resource.cookbook_name
             remote_file.source(@new_resource.response_file)
             remote_file.backup(false)
             provider = Chef::Platform.provider_for_resource(remote_file)
             provider.template_location
           rescue
             Chef::Log.debug("#{@new_resource} fetching preseed file via Template resource failed, fallback to CookbookFile resource")
             remote_file = Chef::Resource::CookbookFile.new(cache_seed_to, run_context)
             remote_file.cookbook_name = @new_resource.cookbook_name
             remote_file.source(@new_resource.response_file)
             remote_file.backup(false)
           end      
   
           remote_file
         end
         
         def expand_options(options)
           options ? " #{options}" : ""
         end
   
       end
     end
   end

lib/chef/monkey_patches/object.rb

   class Object
     unless new.respond_to?(:tap)
       def tap
         yield self
         return self
       end
     end
   end
   

lib/chef/knife/cookbook_site_share.rb

   # Author:: Nuo Yan ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteShare < Knife
   
         deps do
           require 'chef/cookbook_uploader'
           require 'chef/cookbook_site_streaming_uploader'
         end
   
         banner "knife cookbook site share COOKBOOK CATEGORY (options)"
         category "cookbook site"
   
         option :cookbook_path,
           :short => "-o PATH:PATH",
           :long => "--cookbook-path PATH:PATH",
           :description => "A colon-separated path to look for cookbooks in",
           :proc => lambda { |o| Chef::Config.cookbook_path = o.split(":") }
   
         def run
           if @name_args.length < 2
             show_usage
             ui.fatal("You must specify the cookbook name and the category you want to share this cookbook to.")
             exit 1
           end
   
           config[:cookbook_path] ||= Chef::Config[:cookbook_path]
   
           cookbook_name = @name_args[0]
           category = @name_args[1]
           cl = Chef::CookbookLoader.new(config[:cookbook_path])
           if cl.cookbook_exists?(cookbook_name)
             cookbook = cl[cookbook_name]
             Chef::CookbookUploader.new(cookbook,config[:cookbook_path]).validate_cookbook
             tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook)
             begin
               Chef::Log.debug("Temp cookbook directory is #{tmp_cookbook_dir.inspect}")
               ui.info("Making tarball #{cookbook_name}.tgz")
               Chef::Mixin::Command.run_command(:command => "tar -czf #{cookbook_name}.tgz #{cookbook_name}", :cwd => tmp_cookbook_dir)
             rescue => e
               ui.error("Error making tarball #{cookbook_name}.tgz: #{e.message}. Set log level to debug (-l debug) for more information.")
               Chef::Log.debug("\n#{e.backtrace.join("\n")}")
               exit(1)
             end
   
             begin
               do_upload("#{tmp_cookbook_dir}/#{cookbook_name}.tgz", category, Chef::Config[:node_name], Chef::Config[:client_key])
               ui.info("Upload complete!")
               Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
               FileUtils.rm_rf tmp_cookbook_dir
             rescue => e
               ui.error("Error uploading cookbook #{cookbook_name} to the Opscode Cookbook Site: #{e.message}. Set log level to debug (-l debug) for more information.")
               Chef::Log.debug("\n#{e.backtrace.join("\n")}")
               exit(1)
             end
   
           else
             ui.error("Could not find cookbook #{cookbook_name} in your cookbook path.")
             exit(1)
           end
   
         end
   
         def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
            uri = "http://cookbooks.opscode.com/api/v1/cookbooks"
   
            category_string = { 'category'=>cookbook_category }.to_json
   
            http_resp = Chef::CookbookSiteStreamingUploader.post(uri, user_id, user_secret_filename, {
              :tarball => File.open(cookbook_filename),
              :cookbook => category_string
            })
   
            res = Chef::JSONCompat.from_json(http_resp.body)
            if http_resp.code.to_i != 201
              if res['error_messages']
                if res['error_messages'][0] =~ /Version already exists/
                  ui.error "The same version of this cookbook already exists on the Opscode Cookbook Site."
                  exit(1)
                else
                  ui.error "#{res['error_messages'][0]}"
                  exit(1)
                end
              else
                ui.error "Unknown error while sharing cookbook"
                ui.error "Server response: #{http_resp.body}"
                exit(1)
              end
            end
            res
          end
       end
   
     end
   end

lib/chef/rest.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Thom May ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'net/https'
   require 'uri'
   require 'chef/json_compat'
   require 'tempfile'
   require 'chef/rest/auth_credentials'
   require 'chef/rest/rest_request'
   require 'chef/monkey_patches/string'
   
   class Chef
     # == Chef::REST
     # Chef's custom REST client with built-in JSON support and RSA signed header
     # authentication.
     class REST
       attr_reader :auth_credentials
       attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit
   
       # Create a REST client object. The supplied +url+ is used as the base for
       # all subsequent requests. For example, when initialized with a base url
       # http://localhost:4000, a call to +get_rest+ with 'nodes' will make an
       # HTTP GET request to http://localhost:4000/nodes
       def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key], options={})
         @url = url
         @cookies = CookieJar.instance
         @default_headers = options[:headers] || {}
         @auth_credentials = AuthCredentials.new(client_name, signing_key_filename)
         @sign_on_redirect, @sign_request = true, true
         @redirects_followed = 0
         @redirect_limit = 10
       end
   
       def signing_key_filename
         @auth_credentials.key_file
       end
   
       def client_name
         @auth_credentials.client_name
       end
   
       def signing_key
         @auth_credentials.raw_key
       end
   
       # Register the client
       #--
       # Requires you to load chef/api_client beforehand. explicit require is removed since
       # most users of this class have no need for chef/api_client. This functionality
       # should be moved anyway...
       def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key])
         if (File.exists?(destination) &&  !File.writable?(destination))
           raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?"
         end
         nc = Chef::ApiClient.new
         nc.name(name)
   
         catch(:done) do
           retries = config[:client_registration_retries] || 5
           0.upto(retries) do |n|
             begin
               response = nc.save(true, true)
               Chef::Log.debug("Registration response: #{response.inspect}")
               raise Chef::Exceptions::CannotWritePrivateKey, "The response from the server did not include a private key!" unless response.has_key?("private_key")
               # Write out the private key
               ::File.open(destination, "w") {|f|
                 f.chmod(0600)
                 f.print(response["private_key"])
               }
               throw :done
             rescue IOError
               raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination}"
             rescue Net::HTTPFatalError => e
               Chef::Log.warn("Failed attempt #{n} of #{retries+1} on client creation")
               raise unless e.response.code == "500"
             end
           end
         end
   
         true
       end
   
       # Send an HTTP GET request to the path
       #
       # Using this method to +fetch+ a file is considered deprecated.
       #
       # === Parameters
       # path:: The path to GET
       # raw:: Whether you want the raw body returned, or JSON inflated.  Defaults
       #   to JSON inflated.
       def get_rest(path, raw=false, headers={})
         if raw
           streaming_request(create_url(path), headers)
         else
           api_request(:GET, create_url(path), headers)
         end
       end
   
       # Send an HTTP DELETE request to the path
       def delete_rest(path, headers={})
         api_request(:DELETE, create_url(path), headers)
       end
   
       # Send an HTTP POST request to the path
       def post_rest(path, json, headers={})
         api_request(:POST, create_url(path), headers, json)
       end
   
       # Send an HTTP PUT request to the path
       def put_rest(path, json, headers={})
         api_request(:PUT, create_url(path), headers, json)
       end
   
       # Streams a download to a tempfile, then yields the tempfile to a block.
       # After the download, the tempfile will be closed and unlinked.
       # If you rename the tempfile, it will not be deleted.
       # Beware that if the server streams infinite content, this method will
       # stream it until you run out of disk space.
       def fetch(path, headers={})
         streaming_request(create_url(path), headers) {|tmp_file| yield tmp_file }
       end
   
       def create_url(path)
         if path =~ /^(http|https):\/\//
           URI.parse(path)
         else
           URI.parse("#{@url}/#{path}")
         end
       end
   
       def sign_requests?
         auth_credentials.sign_requests? && @sign_request
       end
   
       # ==== DEPRECATED
       # Use +api_request+ instead
       #--
       # Actually run an HTTP request.  First argument is the HTTP method,
       # which should be one of :GET, :PUT, :POST or :DELETE.  Next is the
       # URL, then an object to include in the body (which will be converted with
       # .to_json). The limit argument is unused, it is present for backwards
       # compatibility. Configure the redirect limit with #redirect_limit=
       # instead.
       #
       # Typically, you won't use this method -- instead, you'll use one of
       # the helper methods (get_rest, post_rest, etc.)
       #
       # Will return the body of the response on success.
       def run_request(method, url, headers={}, data=false, limit=nil, raw=false)
         json_body = data ? Chef::JSONCompat.to_json(data) : nil
         headers = build_headers(method, url, headers, json_body, raw)
   
         tf = nil
   
         retriable_rest_request(method, url, json_body, headers) do |rest_request|
   
           res = rest_request.call do |response|
             if raw
               tf = stream_to_tempfile(url, response)
             else
               response.read_body
             end
           end
   
           if res.kind_of?(Net::HTTPSuccess)
             if res['content-type'] =~ /json/
               response_body = res.body.chomp
               Chef::JSONCompat.from_json(response_body)
             else
               if method == :HEAD
                 true
               elsif raw
                 tf
               else
                 res.body
               end
             end
           elsif res.kind_of?(Net::HTTPFound) or res.kind_of?(Net::HTTPMovedPermanently)
             follow_redirect {run_request(method, create_url(res['location']), headers, false, nil, raw)}
           elsif res.kind_of?(Net::HTTPNotModified)
             false
           else
             if res['content-type'] =~ /json/
               exception = Chef::JSONCompat.from_json(res.body)
               msg = "HTTP Request Returned #{res.code} #{res.message}: "
               msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
               Chef::Log.warn(msg)
             end
             res.error!
           end
         end
       end
   
       # Runs an HTTP request to a JSON API. File Download not supported.
       def api_request(method, url, headers={}, data=false)
         json_body = data ? Chef::JSONCompat.to_json(data) : nil
         headers = build_headers(method, url, headers, json_body)
   
         retriable_rest_request(method, url, json_body, headers) do |rest_request|
           response = rest_request.call {|r| r.read_body}
   
           if response.kind_of?(Net::HTTPSuccess)
             if response['content-type'] =~ /json/
               Chef::JSONCompat.from_json(response.body.chomp)
             else
               Chef::Log.warn("Expected JSON response, but got content-type '#{response['content-type']}'")
               response.body
             end
           elsif redirect_location = redirected_to(response)
             follow_redirect {api_request(:GET, create_url(redirect_location))}
           else
             if response['content-type'] =~ /json/
               exception = Chef::JSONCompat.from_json(response.body)
               msg = "HTTP Request Returned #{response.code} #{response.message}: "
               msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
               Chef::Log.info(msg)
             end
             response.error!
           end
         end
       end
   
       # Makes a streaming download request. Doesn't speak JSON.
       # Streams the response body to a tempfile. If a block is given, it's
       # passed to Tempfile.open(), which means that the tempfile will automatically
       # be unlinked after the block is executed.
       #
       # If no block is given, the tempfile is returned, which means it's up to
       # you to unlink the tempfile when you're done with it.
       def streaming_request(url, headers, &block)
         headers = build_headers(:GET, url, headers, nil, true)
         retriable_rest_request(:GET, url, nil, headers) do |rest_request|
           tempfile = nil
           response = rest_request.call do |r|
             if block_given? && r.kind_of?(Net::HTTPSuccess)
               begin
                 tempfile = stream_to_tempfile(url, r, &block)
                 yield tempfile
               ensure
                 tempfile.close!
               end
             else
               tempfile = stream_to_tempfile(url, r)
             end
           end
           if response.kind_of?(Net::HTTPSuccess)
             tempfile
           elsif redirect_location = redirected_to(response)
             # TODO: test tempfile unlinked when following redirects.
             tempfile && tempfile.close!
             follow_redirect {streaming_request(create_url(redirect_location), {}, &block)}
           else
             tempfile && tempfile.close!
             response.error!
           end
         end
       end
   
       def retriable_rest_request(method, url, req_body, headers)
         rest_request = Chef::REST::RESTRequest.new(method, url, req_body, headers)
   
         Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
   
         http_attempts = 0
   
         begin
           http_attempts += 1
   
           res = yield rest_request
   
         rescue SocketError, Errno::ETIMEDOUT => e
           e.message.replace "Error connecting to #{url} - #{e.message}"
           raise e
         rescue Errno::ECONNREFUSED
           if http_retry_count - http_attempts + 1 > 0
             Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
             sleep(http_retry_delay)
             retry
           end
           raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
         rescue Timeout::Error
           if http_retry_count - http_attempts + 1 > 0
             Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
             sleep(http_retry_delay)
             retry
           end
           raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
         rescue Net::HTTPFatalError => e
           if http_retry_count - http_attempts + 1 > 0
             sleep_time = 1 + (2 ** http_attempts) + rand(2 ** http_attempts)
             Chef::Log.error("Server returned error for #{url}, retrying #{http_attempts}/#{http_retry_count} in #{sleep_time}s")
             sleep(sleep_time)
             retry
           end
           raise
         end
       end
   
       def authentication_headers(method, url, json_body=nil)
         request_params = {:http_method => method, :path => url.path, :body => json_body, :host => "#{url.host}:#{url.port}"}
         request_params[:body] ||= ""
         auth_credentials.signature_headers(request_params)
       end
   
       def http_retry_delay
         config[:http_retry_delay]
       end
   
       def http_retry_count
         config[:http_retry_count]
       end
   
       def config
         Chef::Config
       end
   
       def follow_redirect
         raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
         @redirects_followed += 1
         Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}")
         if @sign_on_redirect
           yield
         else
           @sign_request = false
           yield
         end
       ensure
         @redirects_followed = 0
         @sign_request = true
       end
   
       private
   
       def redirected_to(response)
         if response.kind_of?(Net::HTTPFound) || response.kind_of?(Net::HTTPMovedPermanently)
           response['location']
         else
           nil
         end
       end
   
       def build_headers(method, url, headers={}, json_body=false, raw=false)
         headers                 = @default_headers.merge(headers)
         headers['Accept']       = "application/json" unless raw
         headers["Content-Type"] = 'application/json' if json_body
         headers['Content-Length'] = json_body.bytesize.to_s if json_body
         headers.merge!(authentication_headers(method, url, json_body)) if sign_requests?
         headers.merge!(Chef::Config[:custom_http_headers]) if Chef::Config[:custom_http_headers]
         headers
       end
   
       def stream_to_tempfile(url, response)
         tf = Tempfile.open("chef-rest")
         if RUBY_PLATFORM =~ /mswin|mingw32|windows/
           tf.binmode #required for binary files on Windows platforms
         end
         Chef::Log.debug("Streaming download from #{url.to_s} to tempfile #{tf.path}")
         # Stolen from http://www.ruby-forum.com/topic/166423
         # Kudos to _why!
         size, total = 0, response.header['Content-Length'].to_i
         response.read_body do |chunk|
           tf.write(chunk)
           size += chunk.size
         end
         tf.close
         tf
       rescue Exception
         tf.close!
         raise
       end
   
     end
   end

lib/chef/provider/user/useradd.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/user'
   
   class Chef
     class Provider
       class User 
         class Useradd < Chef::Provider::User
           UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
   
           def create_user
             command = compile_command("useradd") do |useradd|
               useradd << universal_options
               useradd << useradd_options
             end
             run_command(:command => command)
           end
           
           def manage_user
             command = compile_command("usermod") { |u| u << universal_options }
             run_command(:command => command)
           end
           
           def remove_user
             command = "userdel"
             command << " -r" if managing_home_dir?
             command << " #{@new_resource.username}"
             run_command(:command => command)
           end
           
           def check_lock
             status = popen4("passwd -S #{@new_resource.username}") do |pid, stdin, stdout, stderr|
               status_line = stdout.gets.split(' ')
               case status_line[1]
               when /^P/
                 @locked = false
               when /^N/
                 @locked = false
               when /^L/
                 @locked = true
               end
             end
   
             unless status.exitstatus == 0
               raise_lock_error = false
               # we can get an exit code of 1 even when it's successful on rhel/centos (redhat bug 578534)
               if status.exitstatus == 1 && ['redhat', 'centos'].include?(node[:platform])
                 passwd_version_status = popen4('rpm -q passwd') do |pid, stdin, stdout, stderr|
                   passwd_version = stdout.gets.chomp
   
                   unless passwd_version == 'passwd-0.73-1'
                     raise_lock_error = true
                   end
                 end
               else
                 raise_lock_error = true
               end
   
               raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if raise_lock_error
             end
   
             @locked
           end
           
           def lock_user
             run_command(:command => "usermod -L #{@new_resource.username}")
           end
           
           def unlock_user
             run_command(:command => "usermod -U #{@new_resource.username}")
           end
   
           def compile_command(base_command)
             yield base_command
             base_command << " #{@new_resource.username}"
             base_command
           end
           
           def universal_options
             opts = ''
             
             UNIVERSAL_OPTIONS.each do |field, option|
               if @current_resource.send(field) != @new_resource.send(field)
                 if @new_resource.send(field)
                   Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field)}")
                   opts << " #{option} '#{@new_resource.send(field)}'"
                 end
               end
             end
             if updating_home?
               if managing_home_dir?
                 Chef::Log.debug("#{@new_resource} managing the users home directory")
                 opts << " -d '#{@new_resource.home}'"
               else
                 Chef::Log.debug("#{@new_resource} setting home to #{@new_resource.home}")
                 opts << " -d '#{@new_resource.home}'"
               end
             end
             opts << " -o" if @new_resource.non_unique || @new_resource.supports[:non_unique]
             opts
           end
   
           def useradd_options
             opts = ''
             opts << " -m" if updating_home? && managing_home_dir?
             opts << " -r" if @new_resource.system
             opts
           end
   
           def updating_home?
             @current_resource.home != @new_resource.home && @new_resource.home
           end
   
           def managing_home_dir?
             @new_resource.manage_home || @new_resource.supports[:manage_home]
           end
   
         end
       end
     end
   end

lib/chef/provider/git.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   require 'chef/log'
   require 'chef/provider'
   require 'chef/mixin/shell_out'
   require 'fileutils'
   
   class Chef
     class Provider
       class Git < Chef::Provider
   
         include Chef::Mixin::ShellOut
   
         def load_current_resource
           @current_resource = Chef::Resource::Git.new(@new_resource.name)
           if current_revision = find_current_revision
             @current_resource.revision current_revision
           end
         end
   
         def action_checkout
           assert_target_directory_valid!
   
           if target_dir_non_existent_or_empty?
             clone
             checkout
             enable_submodules
             add_remotes
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory"
           end
         end
   
         def action_export
           action_checkout
           FileUtils.rm_rf(::File.join(@new_resource.destination,".git"))
           @new_resource.updated_by_last_action(true)
         end
   
         def action_sync
           assert_target_directory_valid!
   
           if existing_git_clone?
             current_rev = find_current_revision
             Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{target_revision}"
             unless current_revision_matches_target_revision?
               fetch_updates
               enable_submodules
               Chef::Log.info "#{@new_resource} updated to revision #{target_revision}"
               @new_resource.updated_by_last_action(true)
             end
             add_remotes
           else
             action_checkout
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def assert_target_directory_valid!
           target_parent_directory = ::File.dirname(@new_resource.destination)
           unless ::File.directory?(target_parent_directory)
             msg = "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist"
             raise Chef::Exceptions::MissingParentDirectory, msg
           end
         end
   
         def existing_git_clone?
           ::File.exist?(::File.join(@new_resource.destination, ".git"))
         end
   
         def target_dir_non_existent_or_empty?
           !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
         end
   
         def find_current_revision
           Chef::Log.debug("#{@new_resource} finding current git revision")
           if ::File.exist?(::File.join(cwd, ".git"))
             # 128 is returned when we're not in a git repo. this is fine
             result = shell_out!('git rev-parse HEAD', :cwd => cwd, :returns => [0,128]).stdout.strip
           end
           sha_hash?(result) ? result : nil
         end
   
         def add_remotes
           if (@new_resource.additional_remotes.length > 0)
             @new_resource.additional_remotes.each_pair do |remote_name, remote_url|
               Chef::Log.info "#{@new_resource} adding git remote #{remote_name} = #{remote_url}"
               command = "git remote add #{remote_name} #{remote_url}"
               if shell_out(command, run_options(:cwd => @new_resource.destination, :command_log_level => :info)).exitstatus != 0
                 @new_resource.updated_by_last_action(true)
               end
             end
           end
         end
   
         def clone
           remote = @new_resource.remote
   
           args = []
           args << "-o #{remote}" unless remote == 'origin'
           args << "--depth #{@new_resource.depth}" if @new_resource.depth
   
           Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{@new_resource.destination}"
   
           clone_cmd = "git clone #{args.join(' ')} #{@new_resource.repository} #{@new_resource.destination}"
           shell_out!(clone_cmd, run_options(:command_log_level => :info))
         end
   
         def checkout
           sha_ref = target_revision
           # checkout into a local branch rather than a detached HEAD
           shell_out!("git checkout -b deploy #{sha_ref}", run_options(:cwd => @new_resource.destination))
           Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} reference: #{sha_ref}"
         end
   
         def enable_submodules
           if @new_resource.enable_submodules
             Chef::Log.info "#{@new_resource} enabling git submodules"
             command = "git submodule init && git submodule update"
             shell_out!(command, run_options(:cwd => @new_resource.destination, :command_log_level => :info))
           end
         end
   
         def fetch_updates
           setup_remote_tracking_branches if @new_resource.remote != 'origin'
   
           # since we're in a local branch already, just reset to specified revision rather than merge
           fetch_command = "git fetch #{@new_resource.remote} && git fetch #{@new_resource.remote} --tags && git reset --hard #{target_revision}"
           Chef::Log.debug "Fetching updates from #{new_resource.remote} and resetting to revison #{target_revision}"
           shell_out!(fetch_command, run_options(:cwd => @new_resource.destination))
         end
   
         # Use git-config to setup a remote tracking branches. Could use
         # git-remote but it complains when a remote of the same name already
         # exists, git-config will just silenty overwrite the setting every
         # time. This could cause wierd-ness in the remote cache if the url
         # changes between calls, but as long as the repositories are all
         # based from each other it should still work fine.
         def setup_remote_tracking_branches
           command = []
   
           Chef::Log.debug "#{@new_resource} configuring remote tracking branches for repository #{@new_resource.repository} "+
                           "at remote #{@new_resource.remote}"
           command << "git config remote.#{@new_resource.remote}.url #{@new_resource.repository}"
           command << "git config remote.#{@new_resource.remote}.fetch +refs/heads/*:refs/remotes/#{@new_resource.remote}/*"
           shell_out!(command.join(" && "), run_options(:cwd => @new_resource.destination))
         end
   
         def current_revision_matches_target_revision?
           (!@current_resource.revision.nil?) && (target_revision.strip.to_i(16) == @current_resource.revision.strip.to_i(16))
         end
   
         def target_revision
           @target_revision ||= begin
             assert_revision_not_remote
   
             if sha_hash?(@new_resource.revision)
               @target_revision = @new_resource.revision
             else
               resolved_reference = remote_resolve_reference
               @target_revision = extract_revision(resolved_reference)
             end
           end
         end
   
         alias :revision_slug :target_revision
   
         def remote_resolve_reference
           Chef::Log.debug("#{@new_resource} resolving remote reference")
           command = git('ls-remote', @new_resource.repository, @new_resource.revision)
           shell_out!(command, run_options).stdout
         end
   
         private
   
         def run_options(run_opts={})
           run_opts[:user] = @new_resource.user if @new_resource.user
           run_opts[:group] = @new_resource.group if @new_resource.group
           run_opts[:environment] = {"GIT_SSH" => @new_resource.ssh_wrapper} if @new_resource.ssh_wrapper
           run_opts[:command_log_prepend] = @new_resource.to_s
           run_opts[:command_log_level] ||= :debug
           if run_opts[:command_log_level] == :info
             if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
               run_opts[:live_stream] = STDOUT
             end
           end
           run_opts
         end
   
         def cwd
           @new_resource.destination
         end
   
         def git(*args)
           ["git", *args].compact.join(" ")
         end
   
         def sha_hash?(string)
           string =~ /^[0-9a-f]{40}$/
         end
   
         def assert_revision_not_remote
           if @new_resource.revision =~ /^origin\//
             reference = @new_resource.revision
             error_text =  "Deploying remote branches is not supported. " +
                           "Specify the remote branch as a local branch for " +
                           "the git repository you're deploying from " +
                           "(ie: '#{reference.gsub('origin/', '')}' rather than '#{reference}')."
             raise RuntimeError, error_text
           end
         end
   
         def extract_revision(resolved_reference)
           unless resolved_reference =~ /^([0-9a-f]{40})\s+(\S+)/
             msg = "Unable to parse SHA reference for '#{@new_resource.revision}' in repository '#{@new_resource.repository}'. "
             msg << "Verify your (case-sensitive) repository URL and revision.\n"
             msg << "`git ls-remote` output: #{resolved_reference}"
             raise Chef::Exceptions::UnresolvableGitReference, msg
           end
           $1
         end
   
       end
     end
   end

lib/chef/provider/package/dpkg.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'chef/mixin/get_source_from_package'
   
   class Chef
     class Provider
       class Package
         class Dpkg < Chef::Provider::Package::Apt
           DPKG_INFO = /([a-z\d\-\+]+)\t([\w\d.~-]+)/
           DPKG_INSTALLED = /^Status: install ok installed/
           DPKG_VERSION = /^Version: (.+)$/
   
           include Chef::Mixin::GetSourceFromPackage
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             @new_resource.version(nil)
   
             # if the source was not set, and we're installing, fail
             if Array(@new_resource.action).include?(:install) && @new_resource.source.nil?
               raise Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
             end
   
             # We only -need- source for action install
             if @new_resource.source
               unless ::File.exists?(@new_resource.source)
                 raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
               end
   
               # Get information from the package if supplied
               Chef::Log.debug("#{@new_resource} checking dpkg status")
               status = popen4("dpkg-deb -W #{@new_resource.source}") do |pid, stdin, stdout, stderr|
                 stdout.each_line do |line|
                   if pkginfo = DPKG_INFO.match(line)
                     @current_resource.package_name(pkginfo[1])
                     @new_resource.version(pkginfo[2])
                   end
                 end
               end
             end
             
             # Check to see if it is installed
             package_installed = nil
             Chef::Log.debug("#{@new_resource} checking install state")
             status = popen4("dpkg -s #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each_line do |line|
                 case line
                 when DPKG_INSTALLED
                   package_installed = true
                 when DPKG_VERSION
                   if package_installed
                     Chef::Log.debug("#{@new_resource} current version is #{$1}")
                     @current_resource.version($1)
                   end
                 end
               end
             end
   
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "dpkg failed - #{status.inspect}!"
             end
             
             @current_resource
           end
        
           def install_package(name, version)
             run_command_with_systems_locale(
               :command => "dpkg -i#{expand_options(@new_resource.options)} #{@new_resource.source}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
   
           def remove_package(name, version)
             run_command_with_systems_locale(
               :command => "dpkg -r#{expand_options(@new_resource.options)} #{@new_resource.package_name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
         
           def purge_package(name, version)
             run_command_with_systems_locale(
               :command => "dpkg -P#{expand_options(@new_resource.options)} #{@new_resource.package_name}",
               :environment => {
                 "DEBIAN_FRONTEND" => "noninteractive"
               }
             )
           end
         end
       end
     end
   end

lib/chef/provider/user/dscl.rb

   #
   # Author:: Dreamcat4 ()
   # Copyright:: Copyright (c) 2009 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/shell_out'
   require 'chef/provider/user'
   require 'openssl'
   
   class Chef
     class Provider
       class User
         class Dscl < Chef::Provider::User
           include Chef::Mixin::ShellOut
           
           NFS_HOME_DIRECTORY        = %r{^NFSHomeDirectory: (.*)$}
           AUTHENTICATION_AUTHORITY  = %r{^AuthenticationAuthority: (.*)$}
           
           def dscl(*args)
             shell_out("dscl . -#{args.join(' ')}")
           end
   
           def safe_dscl(*args)
             result = dscl(*args)
             return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 )
             raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") unless result.exitstatus == 0
             raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") if result.stdout =~ /No such key: /
             return result.stdout
           end
   
           # This is handled in providers/group.rb by Etc.getgrnam()
           # def user_exists?(user)
           #   users = safe_dscl("list /Users")
           #   !! ( users =~ Regexp.new("\n#{user}\n") )
           # end
   
           # get a free UID greater than 200
           def get_free_uid(search_limit=1000)
             uid = nil; next_uid_guess = 200
             users_uids = safe_dscl("list /Users uid")
             while(next_uid_guess < search_limit + 200)
               if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n")
                 next_uid_guess += 1
               else
                 uid = next_uid_guess
                 break
               end
             end
             return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
           end
   
           def uid_used?(uid)
             return false unless uid
             users_uids = safe_dscl("list /Users uid")
                ( users_uids =~ Regexp.new("#{Regexp.escape(uid.to_s)}\n") )
           end
   
           def set_uid
             @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
             if uid_used?(@new_resource.uid)
               raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
             end
             safe_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
           end
   
           def modify_home
             return safe_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory") if (@new_resource.home.nil? || @new_resource.home.empty?)
             if @new_resource.supports[:manage_home]
               validate_home_dir_specification!
               
               if (@current_resource.home == @new_resource.home) && !new_home_exists?
                 ditto_home
               elsif !current_home_exists? && !new_home_exists?
                 ditto_home
               elsif current_home_exists?
                 move_home
               end
             end
             safe_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
           end
   
           def osx_shadow_hash?(string)
             return    ( string =~ /^[[:xdigit:]]{1240}$/ )
           end
   
           def osx_salted_sha1?(string)
             return    ( string =~ /^[[:xdigit:]]{48}$/ )
           end
   
           def guid
             safe_dscl("read /Users/#{@new_resource.username} GeneratedUID").gsub(/GeneratedUID: /,"").strip
           end
   
           def shadow_hash_set?
             user_data = safe_dscl("read /Users/#{@new_resource.username}") 
             if user_data =~ /AuthenticationAuthority: / && user_data =~ /ShadowHash/
               true
             else
               false
             end
           end
   
           def modify_password
             if @new_resource.password
               shadow_hash = nil
               
               Chef::Log.debug("#{new_resource} updating password")
               if osx_shadow_hash?(@new_resource.password)
                 shadow_hash = @new_resource.password.upcase
               else
                 if osx_salted_sha1?(@new_resource.password)
                   salted_sha1 = @new_resource.password.upcase
                 else
                   hex_salt = ""
                   OpenSSL::Random.random_bytes(10).each_byte { |b| hex_salt << b.to_i.to_s(16) }
                   hex_salt = hex_salt.slice(0...8)
                   salt = [hex_salt].pack("H*")
                   sha1 = ::OpenSSL::Digest::SHA1.hexdigest(salt+@new_resource.password)
                   salted_sha1 = (hex_salt+sha1).upcase
                 end
                 shadow_hash = String.new("00000000"*155)
                 shadow_hash[168] = salted_sha1
               end
               
               ::File.open("/var/db/shadow/hash/#{guid}",'w',0600) do |output|
                 output.puts shadow_hash
               end
               
               unless shadow_hash_set?
                 safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';ShadowHash;'")
               end
             end
           end
   
           def load_current_resource
             super
             raise Chef::Exceptions::User, "Could not find binary /usr/bin/dscl for #{@new_resource}" unless ::File.exists?("/usr/bin/dscl")
           end
   
           def create_user
             dscl_create_user
             dscl_create_comment
             set_uid
             dscl_set_gid
             modify_home
             dscl_set_shell
             modify_password
           end
           
           def manage_user
             dscl_create_user    if diverged?(:username)
             dscl_create_comment if diverged?(:comment)
             set_uid             if diverged?(:uid)
             dscl_set_gid        if diverged?(:uid)
             modify_home         if diverged?(:home)
             dscl_set_shell      if diverged?(:shell)
             modify_password     if diverged?(:password)
           end
           
           def dscl_create_user
             safe_dscl("create /Users/#{@new_resource.username}")              
           end
           
           def dscl_create_comment
             safe_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
           end
           
           def dscl_set_gid
             safe_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
           end
           
           def dscl_set_shell
             if @new_resource.password || ::File.exists?("#{@new_resource.shell}")
               safe_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
             else
               safe_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
             end
           end
           
           def remove_user
             if @new_resource.supports[:manage_home]
               user_info = safe_dscl("read /Users/#{@new_resource.username}") 
               if nfs_home_match = user_info.match(NFS_HOME_DIRECTORY)
                 #nfs_home = safe_dscl("read /Users/#{@new_resource.username} NFSHomeDirectory")
                 #nfs_home.gsub!(/NFSHomeDirectory: /,"").gsub!(/\n$/,"")
                 nfs_home = nfs_home_match[1]
                 FileUtils.rm_rf(nfs_home)
               end
             end
             # remove the user from its groups
             groups = []
             Etc.group do |group|
               groups << group.name if group.mem.include?(@new_resource.username)
             end
             groups.each do |group_name|
               safe_dscl("delete /Groups/#{group_name} GroupMembership '#{@new_resource.username}'")
             end
             # remove user account
             safe_dscl("delete /Users/#{@new_resource.username}")
           end
   
           def locked?
             user_info = safe_dscl("read /Users/#{@new_resource.username}")
             if auth_authority_md = AUTHENTICATION_AUTHORITY.match(user_info)
                 (auth_authority_md[1] =~ /DisabledUser/ )
             else
               false
             end
           end
           
           def check_lock
             return @locked = locked?
           end
   
           def lock_user
             safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
           end
           
           def unlock_user
             auth_info = safe_dscl("read /Users/#{@new_resource.username} AuthenticationAuthority")
             auth_string = auth_info.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip#.gsub!(/[; ]*$/,"")
             safe_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
           end
           
           def validate_home_dir_specification!
             unless @new_resource.home =~ /^\//
               raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'") 
             end
           end
           
           def current_home_exists?
             ::File.exist?("#{@current_resource.home}")
           end
           
           def new_home_exists?
             ::File.exist?("#{@new_resource.home}")          
           end
           
           def ditto_home
             skel = "/System/Library/User Template/English.lproj"
             raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
             shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
             ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
           end
   
           def move_home
             Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
             
             src = @current_resource.home
             FileUtils.mkdir_p(@new_resource.home)
             files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
             ::FileUtils.mv(files,@new_resource.home, :force => true)
             ::FileUtils.rmdir(src)
             ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
           end
           
           def diverged?(parameter)
             parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
           end
           
           def parameter_updated?(parameter)
             not (@new_resource.send(parameter) == @current_resource.send(parameter))
           end
         end
       end
     end
   end

lib/chef/resource/mount.rb

   #
   # Author:: Joshua Timberman ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Mount < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :mount
           @mount_point = name
           @device = nil
           @device_type = :device
           @fstype = "auto"
           @options = ["defaults"]
           @dump = 0
           @pass = 2
           @mounted = false
           @enabled = false
           @action = :mount
           @supports = { :remount => false }
           @allowed_actions.push(:mount, :umount, :remount, :enable, :disable)
         end
         
         def mount_point(arg=nil)
           set_or_return(
             :mount_point,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def device(arg=nil)
           set_or_return(
             :device,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def device_type(arg=nil)
           real_arg = arg.kind_of?(String) ? arg.to_sym : arg
           set_or_return(
             :device_type,
             real_arg,
             :equal_to => [ :device, :label, :uuid ]
           )
         end
   
         def fstype(arg=nil)
           set_or_return(
             :fstype,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def options(arg=nil)
           if arg.is_a?(String)
             converted_arg = arg.gsub(/,/, ' ').split(/ /)
           else
             converted_arg = arg
           end
           set_or_return(
             :options,
             converted_arg,
             :kind_of => [ Array ]
           )
         end
         
         def dump(arg=nil)
           set_or_return(
             :dump,
             arg,
             :kind_of => [ Integer, FalseClass ]
           )
         end
         
         def pass(arg=nil)
           set_or_return(
             :pass,
             arg,
             :kind_of => [ Integer, FalseClass ]
           )
         end
         
         def mounted(arg=nil)
           set_or_return(
             :mounted,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def enabled(arg=nil)
           set_or_return(
             :enabled,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
           
         def supports(args={})
           if args.is_a? Array
             args.each { |arg| @supports[arg] = true }
           elsif args.any?
             @supports = args
           else
             @supports
           end
         end
         
       end
     end
   end
   
           

lib/chef/resource/ifconfig.rb

   #
   # Author:: Jason K. Jackson (jasonjackson@gmail.com)
   # Copyright:: Copyright (c) 2009 Jason K. Jackson
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Ifconfig < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :ifconfig
           @target = name
           @action = :add
           @allowed_actions.push(:add, :delete, :enable, :disable)
           @hwaddr = nil
           @mask = nil
           @inet_addr = nil
           @bcast = nil
           @mtu = nil
           @metric = nil
           @device = nil 
           @onboot = nil
           @network = nil
           @bootproto = nil
           @onparent = nil
         end
   
         def target(arg=nil)
           set_or_return(
             :target,
             arg,
             :kind_of => String
           )
         end
   
         def device(arg=nil)
           set_or_return(
             :device,
             arg,
             :kind_of => String
           )
         end
   
         def hwaddr(arg=nil)
           set_or_return(
             :hwaddr,
             arg,
             :kind_of => String
           )
         end
   
         def inet_addr(arg=nil)
           set_or_return(
             :inet_addr,
             arg,
             :kind_of => String
           )
         end
   
         def bcast(arg=nil)
           set_or_return(
             :bcast,
             arg,
             :kind_of => String
           )
         end
   
         def mask(arg=nil)
           set_or_return(
             :mask,
             arg,
             :kind_of => String
           )
         end
   
         def mtu(arg=nil)
           set_or_return(
             :mtu,
             arg,
             :kind_of => String
           )
         end
   
         def metric(arg=nil)
           set_or_return(
             :metric,
             arg,
             :kind_of => String
           )
         end
   
         def onboot(arg=nil)
           set_or_return(
             :onboot,
             arg,
             :kind_of => String
           )
         end
   
         def network(arg=nil)
           set_or_return(
             :network,
             arg,
             :kind_of => String
           )
         end
   
         def bootproto(arg=nil)
           set_or_return(
             :bootproto,
             arg,
             :kind_of => String
           )
         end
   
         def onparent(arg=nil)
           set_or_return(
             :onparent,
             arg,
             :kind_of => String
           )
         end
       end
   
     end
   end
   
   

lib/chef/resource/execute.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Execute < Chef::Resource
   
         def initialize(name, run_context=nil)
           super
           @resource_name = :execute
           @command = name
           @backup = 5
           @action = "run"
           @creates = nil
           @cwd = nil
           @environment = nil
           @group = nil
           @path = nil
           @returns = 0
           @timeout = nil
           @user = nil
           @allowed_actions.push(:run)
           @umask = nil
         end
   
         def umask(arg=nil)
           set_or_return(
             :umask,
             arg,
             :kind_of => [ String, Integer ]
           )
         end
       
         def command(arg=nil)
           set_or_return(
             :command,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def creates(arg=nil)
           set_or_return(
             :creates,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def cwd(arg=nil)
           set_or_return(
             :cwd,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def environment(arg=nil)
           set_or_return(
             :environment,
             arg,
             :kind_of => [ Hash ]
           )
         end
         
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :kind_of => [ String, Integer ]
           )
         end
   
         def path(arg=nil)
           set_or_return(
             :path,
             arg,
             :kind_of => [ Array ]
           )
         end
         
         def returns(arg=nil)
           set_or_return(
             :returns,
             arg,
             :kind_of => [ Integer, Array ]
           )
         end
         
         def timeout(arg=nil)
           set_or_return(
             :timeout,
             arg,
             :kind_of => [ Integer ]
           )
         end
         
         def user(arg=nil)
           set_or_return(
             :user,
             arg,
             :kind_of => [ String, Integer ]
           )
         end
   
   
   
       end
     end
   end

lib/chef/provider/user.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider'
   require 'chef/mixin/command'
   require 'chef/resource/user'
   require 'etc'
   
   class Chef
     class Provider
       class User < Chef::Provider
         
         include Chef::Mixin::Command
         
         attr_accessor :user_exists, :locked
         
         def initialize(new_resource, run_context)
           super
           @user_exists = true
           @locked = nil
         end
     
         def convert_group_name
           if @new_resource.gid.is_a? String
             @new_resource.gid(Etc.getgrnam(@new_resource.gid).gid)
           end
         rescue ArgumentError => e
           raise Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}"
         end
         
         def load_current_resource
           @current_resource = Chef::Resource::User.new(@new_resource.name)
           @current_resource.username(@new_resource.username)
         
           begin
             user_info = Etc.getpwnam(@new_resource.username)
           rescue ArgumentError => e
             @user_exists = false
             Chef::Log.debug("#{@new_resource} user does not exist")
             user_info = nil
           end
           
           if user_info
             @current_resource.uid(user_info.uid)
             @current_resource.gid(user_info.gid)
             @current_resource.comment(user_info.gecos)
             @current_resource.home(user_info.dir)
             @current_resource.shell(user_info.shell)
             @current_resource.password(user_info.passwd)
           
             if @new_resource.password && @current_resource.password == 'x'
               begin
                 require 'shadow'
               rescue LoadError
                 raise Chef::Exceptions::MissingLibrary, "You must have ruby-shadow installed for password support!"
               else
                 shadow_info = Shadow::Passwd.getspnam(@new_resource.username)
                 @current_resource.password(shadow_info.sp_pwdp)
               end
             end
             
             if @new_resource.gid
               convert_group_name
             end
           end
           
           @current_resource
         end
   
         # Check to see if the user needs any changes
         #
         # === Returns
         # :: If a change is required
         # :: If the users are identical
         def compare_user
           [ :uid, :gid, :comment, :home, :shell, :password ].any? do |user_attrib|
             !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
           end
         end
         
         def action_create
           if !@user_exists
             create_user
             Chef::Log.info("#{@new_resource} created")
             @new_resource.updated_by_last_action(true)
           elsif compare_user
             manage_user
             Chef::Log.info("#{@new_resource} altered")
             @new_resource.updated_by_last_action(true)
           end
         end
         
         def action_remove
           if @user_exists
             remove_user
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} removed")
           end
         end
   
         def remove_user
           raise NotImplementedError
         end
   
         def action_manage
           if @user_exists && compare_user
             manage_user
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} managed")
           end
         end
   
         def manage_user
           raise NotImplementedError
         end
   
         def action_modify
           if @user_exists
             if compare_user
               manage_user
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} modified")
             end
           else
             raise Chef::Exceptions::User, "Cannot modify user - does not exist!"
           end
         end
   
         def action_lock
           if @user_exists
             if check_lock() == false
               lock_user
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} locked")
             else
               Chef::Log.debug("#{@new_resource} already locked - nothing to do")
             end
           else
             raise Chef::Exceptions::User, "Cannot lock user - does not exist!"
           end
         end
   
         def check_lock
           raise NotImplementedError
         end
   
         def lock_user
           raise NotImplementedError
         end
   
         def action_unlock
           if @user_exists
             if check_lock() == true
               unlock_user
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} unlocked")
             else
               Chef::Log.debug("#{@new_resource} already unlocked - nothing to do")
             end
           else
             raise Chef::Exceptions::User, "Cannot unlock user - does not exist!"
           end
         end
         
         def unlock_user
           raise NotImplementedError
         end
   
       end
     end
   end

lib/chef/provider/service/windows.rb

   #
   # Author:: Nuo Yan 
   # Author:: Bryan McLellan 
   # Author:: Seth Chisamore 
   # Copyright:: Copyright (c) 2010-2011 Opscode, Inc
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service/simple'
   if RUBY_PLATFORM =~ /mswin|mingw32|windows/
     require 'win32/service'
   end
   
   class Chef::Provider::Service::Windows < Chef::Provider::Service
     RUNNING = 'running'
     STOPPED = 'stopped'
     AUTO_START = 'auto start'
     DISABLED = 'disabled'
   
     def load_current_resource
       @current_resource = Chef::Resource::Service.new(@new_resource.name)
       @current_resource.service_name(@new_resource.service_name)    
       @current_resource.running(current_state == RUNNING)
       Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
       @current_resource.enabled(start_type == AUTO_START)
       Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
       @current_resource
     end
   
     def start_service
       if Win32::Service.exists?(@new_resource.service_name)
         if current_state == RUNNING
           Chef::Log.debug "#{@new_resource} already started - nothing to do"
         else
           if @new_resource.start_command
             Chef::Log.debug "#{@new_resource} starting service using the given start_command"
             shell_out!(@new_resource.start_command)
           else
             spawn_command_thread do
               Win32::Service.start(@new_resource.service_name)
               wait_for_state(RUNNING)
             end
           end
           @new_resource.updated_by_last_action(true)
         end
       else
           Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
       end
     end
   
     def stop_service
       if Win32::Service.exists?(@new_resource.service_name)
         if current_state == RUNNING
           if @new_resource.stop_command
             Chef::Log.debug "#{@new_resource} stopping service using the given stop_command"
             shell_out!(@new_resource.stop_command)
           else
             spawn_command_thread do
               Win32::Service.stop(@new_resource.service_name)
               wait_for_state(STOPPED)
             end
           end
           @new_resource.updated_by_last_action(true)
         else
           Chef::Log.debug "#{@new_resource} already stopped - nothing to do"
         end
       else
         Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
       end
     end
   
     def restart_service
       if Win32::Service.exists?(@new_resource.service_name)
         if @new_resource.restart_command
           Chef::Log.debug "#{@new_resource} restarting service using the given restart_command"
           shell_out!(@new_resource.restart_command)
         else
           stop_service
           start_service
         end
         @new_resource.updated_by_last_action(true)
       else
         Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
       end
     end
   
     def enable_service
       if Win32::Service.exists?(@new_resource.service_name)
         if start_type == AUTO_START
           Chef::Log.debug "#{@new_resource} already enabled - nothing to do"
         else
           Win32::Service.configure(
             :service_name => @new_resource.service_name,
             :start_type => Win32::Service::AUTO_START
           )
           @new_resource.updated_by_last_action(true)
         end
       else
         Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
       end
     end
   
     def disable_service
       if Win32::Service.exists?(@new_resource.service_name)
         if start_type == AUTO_START
           Win32::Service.configure(
             :service_name => @new_resource.service_name,
             :start_type => Win32::Service::DISABLED
           )
           @new_resource.updated_by_last_action(true)
         else
           Chef::Log.debug "#{@new_resource} already disabled - nothing to do"
         end
       else
         Chef::Log.debug "#{@new_resource} does not exist - nothing to do"
       end
     end
   
     private
     def current_state
       Win32::Service.status(@new_resource.service_name).current_state
     end
   
     def start_type
       Win32::Service.config_info(@new_resource.service_name).start_type
     end
   
     # Helper method that waits for a status to change its state since state
     # changes aren't usually instantaneous.
     def wait_for_state(desired_state)
       sleep 1 until current_state == desired_state
     end
   
     # There ain't no party like a thread party...
     def spawn_command_thread
       worker = Thread.new do
         yield
       end
       Timeout.timeout(60) do
         worker.join
       end
     end
   end

lib/chef/knife/cookbook_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookDelete < Knife
   
         attr_accessor :cookbook_name, :version
   
         deps do
           require 'chef/cookbook_version'
         end
   
         option :all, :short => '-a', :long => '--all', :boolean => true, :description => 'delete all versions'
   
         option :purge, :short => '-p', :long => '--purge', :boolean => true, :description => 'Permanently remove files from backing data store'
   
         banner "knife cookbook delete COOKBOOK VERSION (options)"
   
         def run
           confirm("Files that are common to multiple cookbooks are shared, so purging the files may disable other cookbooks. Are you sure you want to purge files instead of just deleting the cookbook") if config[:purge]
           @cookbook_name, @version = name_args
           if @cookbook_name && @version
             delete_explicit_version
           elsif @cookbook_name && config[:all]
             delete_all_versions
           elsif @cookbook_name && @version.nil?
             delete_without_explicit_version
           elsif @cookbook_name.nil?
             show_usage
             ui.fatal("You must provide the name of the cookbook to delete")
             exit(1)
           end
         end
   
         def delete_explicit_version
           delete_object(Chef::CookbookVersion, "#{@cookbook_name} version #{@version}", "cookbook") do
             delete_request("cookbooks/#{@cookbook_name}/#{@version}")
           end
         end
   
         def delete_all_versions
           confirm("Do you really want to delete all versions of #{@cookbook_name}")
           delete_all_without_confirmation
         end
   
         def delete_all_without_confirmation
           # look up the available versions again just in case the user
           # got to the list of versions to delete and selected 'all'
           # and also a specific version
           @available_versions = nil
           Array(available_versions).each do |version|
             delete_version_without_confirmation(version)
           end
         end
   
         def delete_without_explicit_version
           if available_versions.nil?
             # we already logged an error or 2 about it, so just bail
             exit(1)
           elsif available_versions.size == 1
             @version = available_versions.first
             delete_explicit_version
           else
             versions_to_delete = ask_which_versions_to_delete
             delete_versions_without_confirmation(versions_to_delete)
           end
         end
   
         def available_versions
           @available_versions ||= rest.get_rest("cookbooks/#{@cookbook_name}").map do |name, url_and_version|
             url_and_version["versions"].map {|url_by_version| url_by_version["version"]}
           end.flatten
         rescue Net::HTTPServerException => e
           if e.to_s =~ /^404/
             ui.error("Cannot find a cookbook named #{@cookbook_name} to delete")
             nil
           else
             raise
           end
         end
   
         def ask_which_versions_to_delete
           question = "Which version(s) do you want to delete?\n"
           valid_responses = {}
           available_versions.each_with_index do |version, index|
             valid_responses[(index + 1).to_s] = version
             question << "#{index + 1}. #{@cookbook_name} #{version}\n"
           end
           valid_responses[(available_versions.size + 1).to_s] = :all
           question << "#{available_versions.size + 1}. All versions\n\n"
           responses = ask_question(question).split(',').map { |response| response.strip }
   
           if responses.empty?
             ui.error("No versions specified, exiting")
             exit(1)
           end
           versions = responses.map do |response|
             if version = valid_responses[response]
               version
             else
               ui.error("#{response} is not a valid choice, skipping it")
             end
           end
           versions.compact
         end
   
         def delete_version_without_confirmation(version)
           object = delete_request("cookbooks/#{@cookbook_name}/#{version}")
           output(format_for_display(object)) if config[:print_after]
           ui.info("Deleted cookbook[#{@cookbook_name}][#{version}]")
         end
   
         def delete_versions_without_confirmation(versions)
           versions.each do |version|
             if version == :all
               delete_all_without_confirmation
               break
             else
               delete_version_without_confirmation(version)
             end
           end
         end
   
         private
   
         def delete_request(path)
           path += "?purge=true" if config[:purge]
           rest.delete_rest(path)
         end
   
       end
     end
   end

lib/chef/provider/service/arch.rb

   #
   # Author:: Jan Zimmek ()
   # Copyright:: Copyright (c) 2010 Jan Zimmek
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service/init'
   require 'chef/mixin/command'
   
   class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
   
     def initialize(new_resource, run_context)
       super
       @init_command = "/etc/rc.d/#{@new_resource.service_name}"
     end
   
     def load_current_resource
   
       raise Chef::Exceptions::Service unless ::File.exists?("/etc/rc.conf")
       raise Chef::Exceptions::Service unless ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
   
       super
   
       @current_resource.enabled(daemons.include?(@current_resource.service_name))
   
       @current_resource
     end
   
     # Get list of all daemons from the file '/etc/rc.conf'.
     # Mutiple lines and background form are supported. Example:
     #   DAEMONS=(\
     #     foobar \
     #     @example \
     #     !net \
     #   )
     def daemons
       entries = []
       if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
         entries += $1.gsub(/\\?[\r\n]/, ' ').gsub(/# *[^ ]+/,' ').split(' ') if $1.length > 0
       end
   
       yield(entries) if block_given?
   
       entries
     end
   
     # FIXME: Multiple entries of DAEMONS will cause very bad results :)
     def update_daemons(entries)
       content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(' ')})")
       ::File.open("/etc/rc.conf", "w") do |f|
         f.write(content)
       end
     end
   
     def enable_service()
       new_daemons = []
       entries = daemons
   
       if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
         # exists and already enabled (or already enabled as a background service)
         # new_daemons += entries
       else
         if entries.include?("!#{new_resource.service_name}")
           # exists but disabled
           entries.each do |daemon|
             if daemon == "!#{new_resource.service_name}"
               new_daemons << new_resource.service_name
             else
               new_daemons << daemon
             end
           end
         else
           # does not exist
           new_daemons += entries
           new_daemons << new_resource.service_name
         end
         update_daemons(new_daemons)
       end
     end
   
     def disable_service()
       new_daemons = []
       entries = daemons
   
       if entries.include?("!#{new_resource.service_name}")
         # exists and disabled
         # new_daemons += entries
       else
         if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
           # exists but enabled (or enabled as a back-ground service)
           # FIXME: Does arch support !@foobar ?
           entries.each do |daemon|
             if [new_resource.service_name, "@#{new_resource.service_name}"].include?(daemon)
               new_daemons << "!#{new_resource.service_name}"
             else
               new_daemons << daemon
             end
           end
         end
         update_daemons(new_daemons)
       end
     end
   
   end

lib/chef/knife/configure.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class Configure < Knife
         attr_reader :chef_server, :new_client_name, :admin_client_name, :admin_client_key
         attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key
   
         deps do
           require 'ohai'
           Chef::Knife::ClientCreate.load_deps
         end
   
         banner "knife configure (options)"
   
         option :repository,
           :short => "-r REPO",
           :long => "--repository REPO",
           :description => "The path to your chef-repo"
   
         option :initial,
           :short => "-i",
           :long => "--initial",
           :boolean => true,
           :description => "Create an initial API Client"
   
         def configure_chef
           # We are just faking out the system so that you can do this without a key specified
           Chef::Config[:node_name] = 'woot'
           super
           Chef::Config[:node_name] = nil
         end
   
         def run
           ask_user_for_config_path
   
           FileUtils.mkdir_p(chef_config_path)
   
           ask_user_for_config
   
           ::File.open(config[:config_file], "w") do |f|
             f.puts <<-EOH
   log_level                :info
   log_location             STDOUT
   node_name                '#{new_client_name}'
   client_key               '#{new_client_key}'
   validation_client_name   '#{validation_client_name}'
   validation_key           '#{validation_key}'
   chef_server_url          '#{chef_server}'
   cache_type               'BasicFile'
   cache_options( :path => '#{File.join(chef_config_path, "checksums")}' )
   EOH
             unless chef_repo.empty?
               f.puts "cookbook_path [ '#{chef_repo}/cookbooks' ]"
             end
           end
   
           if config[:initial]
             ui.msg("Creating initial API user...")
             Chef::Config[:chef_server_url] = chef_server
             Chef::Config[:node_name] = admin_client_name
             Chef::Config[:client_key] = admin_client_key
             client_create = Chef::Knife::ClientCreate.new
             client_create.name_args = [ new_client_name ]
             client_create.config[:admin] = true
             client_create.config[:file] = new_client_key
             client_create.config[:yes] = true
             client_create.config[:no_editor] = true
             client_create.run
           else
             ui.msg("*****")
             ui.msg("")
             ui.msg("You must place your client key in:")
             ui.msg("  #{new_client_key}")
             ui.msg("Before running commands with Knife!")
             ui.msg("")
             ui.msg("*****")
             ui.msg("")
             ui.msg("You must place your validation key in:")
             ui.msg("  #{validation_key}")
             ui.msg("Before generating instance data with Knife!")
             ui.msg("")
             ui.msg("*****")
           end
   
           ui.msg("Configuration file written to #{config[:config_file]}")
         end
   
         def ask_user_for_config_path
           config[:config_file] ||= ask_question("Where should I put the config file? ", :default => '~/.chef/knife.rb')
           # have to use expand path to expand the tilde character to the user's home
           config[:config_file] = File.expand_path(config[:config_file])
           if File.exists?(config[:config_file])
             confirm("Overwrite #{config[:config_file]}")
           end
         end
   
         def ask_user_for_config
           server_name = guess_servername
           @chef_server            = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "http://#{server_name}:4000")
           if config[:initial]
             @new_client_name        = config[:node_name] || ask_question("Please enter a clientname for the new client: ", :default => Etc.getlogin)
             @admin_client_name      = config[:admin_client_name] || ask_question("Please enter the existing admin clientname: ", :default => 'chef-webui')
             @admin_client_key       = config[:admin_client_key] || ask_question("Please enter the location of the existing admin client's private key: ", :default => '/etc/chef/webui.pem')
           else
             @new_client_name        = config[:node_name] || ask_question("Please enter an existing username or clientname for the API: ", :default => Etc.getlogin)
           end
           @validation_client_name = config[:validation_client_name] || ask_question("Please enter the validation clientname: ", :default => 'chef-validator')
           @validation_key         = config[:validation_key] || ask_question("Please enter the location of the validation key: ", :default => '/etc/chef/validation.pem')
           @chef_repo              = config[:repository] || ask_question("Please enter the path to a chef repository (or leave blank): ")
   
           @new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem")
         end
   
         def guess_servername
           o = Ohai::System.new
           o.require_plugin 'os'
           o.require_plugin 'hostname'
           o[:fqdn] || 'localhost'
         end
   
         def config_file
           config[:config_file]
         end
   
         def chef_config_path
           File.dirname(config_file)
         end
       end
     end
   end

lib/chef/provider/package/rpm.rb

   #
   # Author:: Joshua Timberman ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'chef/mixin/get_source_from_package'
   
   class Chef
     class Provider
       class Package
         class Rpm < Chef::Provider::Package
   
           include Chef::Mixin::GetSourceFromPackage
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             @new_resource.version(nil)
             
             if @new_resource.source
               unless ::File.exists?(@new_resource.source)
                 raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
               end
               
               Chef::Log.debug("#{@new_resource} checking rpm status")
               status = popen4("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}") do |pid, stdin, stdout, stderr|
                 stdout.each do |line|
                   case line
                   when /([\w\d_.-]+)\s([\w\d_.-]+)/
                     @current_resource.package_name($1)
                     @new_resource.version($2)
                   end
                 end
               end
             else
               if Array(@new_resource.action).include?(:install)
                 raise Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
               end
             end
             
             Chef::Log.debug("#{@new_resource} checking install state")
             status = popen4("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                 when /([\w\d_.-]+)\s([\w\d_.-]+)/
                   Chef::Log.debug("#{@new_resource} current version is #{$2}")
                   @current_resource.version($2)
                 end
               end
             end
             
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "rpm failed - #{status.inspect}!"
             end
             
             @current_resource
           end
           
           def install_package(name, version)
             unless @current_resource.version
               run_command_with_systems_locale(
                 :command => "rpm #{@new_resource.options} -i #{@new_resource.source}"
               )
             else
               run_command_with_systems_locale(
                 :command => "rpm #{@new_resource.options} -U #{@new_resource.source}"
               )
             end
           end
           
           alias_method :upgrade_package, :install_package
           
           def remove_package(name, version)
             if version
               run_command_with_systems_locale(
                 :command => "rpm #{@new_resource.options} -e #{name}-#{version}"
               )
             else
               run_command_with_systems_locale(
                 :command => "rpm #{@new_resource.options} -e #{name}"
               )
             end
           end
   
         end
       end
     end
   end
   

lib/chef/cookbook_version.rb

   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Walters ()
   # Author:: Tim Hinderliter ()
   # Author:: Seth Falcon ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright 2008-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/log'
   require 'chef/client'
   require 'chef/node'
   require 'chef/resource_definition_list'
   require 'chef/recipe'
   require 'chef/cookbook/file_vendor'
   require 'chef/checksum'
   require 'chef/cookbook/metadata'
   require 'chef/version_class'
   
   class Chef
   
     #== Chef::MinimalCookbookVersion
     # MinimalCookbookVersion is a duck type of CookbookVersion, used
     # internally by Chef Server as an optimization when determining the
     # optimal cookbook set for a chef-client.
     #
     # MinimalCookbookVersion objects contain only enough information to
     # solve the cookbook collection for a given run list. They *do not*
     # contain enough information to generate the response.
     #
     # See also: Chef::CookbookVersionSelector
     class MinimalCookbookVersion
   
       include Comparable
   
       ID   = "id".freeze
       NAME = 'name'.freeze
       KEY  = 'key'.freeze
       VERSION = 'version'.freeze
       VALUE   = 'value'.freeze
       DEPS    = 'deps'.freeze
   
       DEPENDENCIES      = 'dependencies'.freeze
   
       # Loads the full list of cookbooks, using a couchdb view to fetch
       # only the id, name, version, and dependency constraints. This is
       # enough information to solve for the cookbook collection for a
       # given run list. After solving for the cookbook collection, you
       # need to call +load_full_versions_of+ to convert
       # MinimalCookbookVersion objects to their non-minimal counterparts
       def self.load_all(couchdb)
         # Example:
         # {"id"=>"1a806f1c-b409-4d8e-abab-fa414ff5b96d", "key"=>"activemq", "value"=>{"version"=>"0.3.3", "deps"=>{"java"=>">= 0.0.0", "runit"=>">= 0.0.0"}}}
         couchdb ||= Chef::CouchDB.new
         couchdb.get_view("cookbooks", "all_with_version_and_deps")["rows"].map {|params| self.new(params) }
       end
   
       # Loads the non-minimal CookbookVersion objects corresponding to
       # +minimal_cookbook_versions+ from couchdb using a bulk GET.
       def self.load_full_versions_of(minimal_cookbook_versions, couchdb)
         database_ids = Array(minimal_cookbook_versions).map {|mcv| mcv.couchdb_id }
         couchdb ||= Chef::CouchDB.new
         couchdb.bulk_get(*database_ids)
       end
   
       attr_reader :couchdb_id
       attr_reader :name
       attr_reader :version
       attr_reader :deps
   
       def initialize(params)
         @couchdb_id = params[ID]
         @name = params[KEY]
         @version = params[VALUE][VERSION]
         @deps    = params[VALUE][DEPS]
       end
   
       # Returns the Cookbook::MinimalMetadata object for this cookbook
       # version.
       def metadata
         @metadata ||= Cookbook::MinimalMetadata.new(@name, DEPENDENCIES => @deps)
       end
   
       def legit_version
         @legit_version ||= Chef::Version.new(@version)
       end
   
       def <=>(o)
         raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
         raise "Unexpected comparison to #{o}" unless o.respond_to?(:legit_version)
         legit_version <=> o.legit_version
       end
     end
   
     # == Chef::CookbookVersion
     # CookbookVersion is a model object encapsulating the data about a Chef
     # cookbook. Chef supports maintaining multiple versions of a cookbook on a
     # single server; each version is represented by a distinct instance of this
     # class.
     #--
     # TODO: timh/cw: 5-24-2010: mutators for files (e.g., recipe_filenames=,
     # recipe_filenames.insert) should dirty the manifest so it gets regenerated.
     class CookbookVersion
       include Chef::IndexQueue::Indexable
       include Comparable
   
       COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
   
       DESIGN_DOCUMENT = {
         "version" => 8,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           },
           "all_with_version" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.cookbook_name, doc.version);
               }
             }
             EOJS
           },
           "all_with_version_and_deps" => {
             "map" => <<-JS
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.cookbook_name, {version: doc.version, deps: doc.metadata.dependencies});
               }
             }
             JS
           },
           "all_latest_version" => {
             "map" => %q@
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.cookbook_name, doc.version);
               }
             }
             @,
             "reduce" => %q@
             function(keys, values, rereduce) {
               var result = null;
   
               for (var idx in values) {
                 var value = values[idx];
   
                 if (idx == 0) {
                   result = value;
                   continue;
                 }
   
                 var valueParts = value.split('.').map(function(v) { return parseInt(v); });
                 var resultParts = result.split('.').map(function(v) { return parseInt(v); });
   
                 if (valueParts[0] != resultParts[0]) {
                   if (valueParts[0] > resultParts[0]) {
                     result = value;
                   }
                 }
                 else if (valueParts[1] != resultParts[1]) {
                   if (valueParts[1] > resultParts[1]) {
                     result = value;
                   }
                 }
                 else if (valueParts[2] != resultParts[2]) {
                   if (valueParts[2] > resultParts[2]) {
                     result = value;
                   }
                 }
               }
               return result;
             }
             @
           },
           "all_latest_version_by_id" => {
             "map" => %q@
             function(doc) {
               if (doc.chef_type == "cookbook_version") {
                 emit(doc.cookbook_name, {version: doc.version, id:doc._id});
               }
             }
             @,
             "reduce" => %q@
             function(keys, values, rereduce) {
               var result = null;
   
               for (var idx in values) {
                 var value = values[idx];
   
                 if (idx == 0) {
                   result = value;
                   continue;
                 }
   
                 var valueParts = value.version.split('.').map(function(v) { return parseInt(v); });
                 var resultParts = result.version.split('.').map(function(v) { return parseInt(v); });
   
                 if (valueParts[0] != resultParts[0]) {
                   if (valueParts[0] > resultParts[0]) {
                     result = value;
                   }
                 }
                 else if (valueParts[1] != resultParts[1]) {
                   if (valueParts[1] > resultParts[1]) {
                     result = value;
                   }
                 }
                 else if (valueParts[2] != resultParts[2]) {
                   if (valueParts[2] > resultParts[2]) {
                     result = value;
                   }
                 }
               }
               return result;
             }
             @
           },
         }
       }
   
       attr_accessor :root_dir
       attr_accessor :definition_filenames
       attr_accessor :template_filenames
       attr_accessor :file_filenames
       attr_accessor :library_filenames
       attr_accessor :resource_filenames
       attr_accessor :provider_filenames
       attr_accessor :root_filenames
       attr_accessor :name
       attr_accessor :metadata
       attr_accessor :metadata_filenames
       attr_accessor :status
       attr_accessor :couchdb_rev
       attr_accessor :couchdb
   
       attr_reader :couchdb_id
   
       # attribute_filenames also has a setter that has non-default
       # functionality.
       attr_reader :attribute_filenames
   
       # recipe_filenames also has a setter that has non-default
       # functionality.
       attr_reader :recipe_filenames
   
       attr_reader :recipe_filenames_by_name
       attr_reader :attribute_filenames_by_short_filename
   
       # This is the one and only method that knows how cookbook files'
       # checksums are generated.
       def self.checksum_cookbook_file(filepath)
         Chef::ChecksumCache.generate_md5_checksum_for_file(filepath)
       rescue Errno::ENOENT
         Chef::Log.debug("File #{filepath} does not exist, so there is no checksum to generate")
         nil
       end
   
       # Keep track of the filenames that we use in both eager cookbook
       # downloading (during sync_cookbooks) and lazy (during the run
       # itself, through FileVendor). After the run is over, clean up the
       # cache.
       def self.valid_cache_entries
         @valid_cache_entries ||= {}
       end
   
       def self.reset_cache_validity
         @valid_cache_entries = nil
       end
   
       def self.cache
         Chef::FileCache
       end
   
       # Setup a notification to clear the valid_cache_entries when a Chef client
       # run starts
       Chef::Client.when_run_starts do |run_status|
         reset_cache_validity
       end
   
       # Synchronizes all the cookbooks from the chef-server.
       #
       # === Returns
       # true:: Always returns true
       def self.sync_cookbooks(cookbook_hash)
         Chef::Log.info("Loading cookbooks [#{cookbook_hash.keys.sort.join(', ')}]")
         Chef::Log.debug("Cookbooks detail: #{cookbook_hash.inspect}")
   
         clear_obsoleted_cookbooks(cookbook_hash)
   
         # Synchronize each of the node's cookbooks, and add to the
         # valid_cache_entries hash.
         cookbook_hash.values.each do |cookbook|
           sync_cookbook_file_cache(cookbook)
         end
   
         true
       end
   
       # Iterates over cached cookbooks' files, removing files belonging to
       # cookbooks that don't appear in +cookbook_hash+
       def self.clear_obsoleted_cookbooks(cookbook_hash)
         # Remove all cookbooks no longer relevant to this node
         cache.find(File.join(%w{cookbooks ** *})).each do |cache_file|
           cache_file =~ /^cookbooks\/([^\/]+)\//
           unless cookbook_hash.has_key?($1)
             Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
             cache.delete(cache_file)
           end
         end
       end
   
       # Update the file caches for a given cache segment.  Takes a segment name
       # and a hash that matches one of the cookbooks/_attribute_files style
       # remote file listings.
       #
       # === Parameters
       # cookbook:: The cookbook to update
       # valid_cache_entries:: Out-param; Added to this hash are the files that
       # were referred to by this cookbook
       def self.sync_cookbook_file_cache(cookbook)
         Chef::Log.debug("Synchronizing cookbook #{cookbook.name}")
   
         # files and templates are lazily loaded, and will be done later.
         eager_segments = COOKBOOK_SEGMENTS.dup
         eager_segments.delete(:files)
         eager_segments.delete(:templates)
   
         eager_segments.each do |segment|
           segment_filenames = Array.new
           cookbook.manifest[segment].each do |manifest_record|
             # segment = cookbook segment
             # remote_list = list of file hashes
             #
             # We need the list of known good attribute files, so we can delete any that are
             # just laying about.
   
             cache_filename = File.join("cookbooks", cookbook.name, manifest_record['path'])
             valid_cache_entries[cache_filename] = true
   
             current_checksum = nil
             if cache.has_key?(cache_filename)
               current_checksum = checksum_cookbook_file(cache.load(cache_filename, false))
             end
   
             # If the checksums are different between on-disk (current) and on-server
             # (remote, per manifest), do the update. This will also execute if there
             # is no current checksum.
             if current_checksum != manifest_record['checksum']
               raw_file = chef_server_rest.get_rest(manifest_record[:url], true)
   
               Chef::Log.info("Storing updated #{cache_filename} in the cache.")
               cache.move_to(raw_file.path, cache_filename)
             else
               Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
             end
   
             # make the segment filenames a full path.
             full_path_cache_filename = cache.load(cache_filename, false)
             segment_filenames << full_path_cache_filename
           end
   
           # replace segment filenames with a full-path one.
           if segment.to_sym == :recipes
             cookbook.recipe_filenames = segment_filenames
           elsif segment.to_sym == :attributes
             cookbook.attribute_filenames = segment_filenames
           else
             cookbook.segment_filenames(segment).replace(segment_filenames)
           end
         end
       end
   
       def self.cleanup_file_cache
         unless Chef::Config[:solo]
           # Delete each file in the cache that we didn't encounter in the
           # manifest.
           cache.find(File.join(%w{cookbooks ** *})).each do |cache_filename|
             unless valid_cache_entries[cache_filename]
               Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.")
               cache.delete(cache_filename)
             end
           end
         end
       end
   
       # Register a notification to cleanup unused files from cookbooks
       Chef::Client.when_run_completes_successfully do |run_status|
         cleanup_file_cache
       end
   
       # Creates a new Chef::CookbookVersion object.
       #
       # === Returns
       # object:: Duh. :)
       def initialize(name, couchdb=nil)
         @name = name
         @frozen = false
         @attribute_filenames = Array.new
         @definition_filenames = Array.new
         @template_filenames = Array.new
         @file_filenames = Array.new
         @recipe_filenames = Array.new
         @recipe_filenames_by_name = Hash.new
         @library_filenames = Array.new
         @resource_filenames = Array.new
         @provider_filenames = Array.new
         @metadata_filenames = Array.new
         @root_dir = nil
         @root_filenames = Array.new
         @couchdb_id = nil
         @couchdb = couchdb || Chef::CouchDB.new
         @couchdb_rev = nil
         @status = :ready
         @manifest = nil
         @file_vendor = nil
         @metadata = Chef::Cookbook::Metadata.new
       end
   
       def version
         metadata.version
       end
   
       # Indicates if this version is frozen or not. Freezing a coobkook version
       # indicates that a new cookbook with the same name and version number
       # shoule
       def frozen_version?
         @frozen
       end
   
       def freeze_version
         @frozen = true
       end
   
       def version=(new_version)
         manifest["version"] = new_version
         metadata.version(new_version)
       end
   
       # A manifest is a Mash that maps segment names to arrays of manifest
       # records (see #preferred_manifest_record for format of manifest records),
       # as well as describing cookbook metadata. The manifest follows a form
       # like the following:
       #
       #   {
       #     :cookbook_name = "apache2",
       #     :version = "1.0",
       #     :name = "Apache 2"
       #     :metadata = ???TODO: timh/cw: 5-24-2010: describe this format,
       #
       #     :files => [
       #       {
       #         :name => "afile.rb",
       #         :path => "files/ubuntu-9.10/afile.rb",
       #         :checksum => "2222",
       #         :specificity => "ubuntu-9.10"
       #       },
       #     ],
       #     :templates => [ manifest_record1, ... ],
       #     ...
       #   }
       def manifest
         unless @manifest
           generate_manifest
         end
         @manifest
       end
   
       def manifest=(new_manifest)
         @manifest = Mash.new new_manifest
         @checksums = extract_checksums_from_manifest(@manifest)
         @manifest_records_by_path = extract_manifest_records_by_path(@manifest)
   
         COOKBOOK_SEGMENTS.each do |segment|
           next unless @manifest.has_key?(segment)
           filenames = @manifest[segment].map{|manifest_record| manifest_record['name']}
   
           if segment == :recipes
             self.recipe_filenames = filenames
           elsif segment == :attributes
             self.attribute_filenames = filenames
           else
             segment_filenames(segment).clear
             filenames.each { |filename| segment_filenames(segment) << filename }
           end
         end
       end
   
       # Returns a hash of checksums to either nil or the on disk path (which is
       # done by generate_manifest).
       def checksums
         unless @checksums
           generate_manifest
         end
         @checksums
       end
   
       def manifest_records_by_path
         @manifest_records_by_path || generate_manifest
         @manifest_records_by_path
       end
   
       def full_name
         "#{name}-#{version}"
       end
   
       def attribute_filenames=(*filenames)
         @attribute_filenames = filenames.flatten
         @attribute_filenames_by_short_filename = filenames_by_name(attribute_filenames)
         attribute_filenames
       end
   
       ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
       alias :attribute_files :attribute_filenames
       alias :attribute_files= :attribute_filenames=
   
       # Return recipe names in the form of cookbook_name::recipe_name
       def fully_qualified_recipe_names
         results = Array.new
         recipe_filenames_by_name.each_key do |rname|
           results << "#{name}::#{rname}"
         end
         results
       end
   
       def recipe_filenames=(*filenames)
         @recipe_filenames = filenames.flatten
         @recipe_filenames_by_name = filenames_by_name(recipe_filenames)
         recipe_filenames
       end
   
       ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
       alias :recipe_files :recipe_filenames
       alias :recipe_files= :recipe_filenames=
   
       # called from DSL
       def load_recipe(recipe_name, run_context)
         unless recipe_filenames_by_name.has_key?(recipe_name)
           raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{name}"
         end
   
         Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}")
         recipe = Chef::Recipe.new(name, recipe_name, run_context)
         recipe_filename = recipe_filenames_by_name[recipe_name]
   
         unless recipe_filename
           raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
         end
   
         recipe.from_file(recipe_filename)
         recipe
       end
   
       def segment_filenames(segment)
         unless COOKBOOK_SEGMENTS.include?(segment)
           raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}"
         end
   
         case segment.to_sym
         when :resources
           @resource_filenames
         when :providers
           @provider_filenames
         when :recipes
           @recipe_filenames
         when :libraries
           @library_filenames
         when :definitions
           @definition_filenames
         when :attributes
           @attribute_filenames
         when :files
           @file_filenames
         when :templates
           @template_filenames
         when :root_files
           @root_filenames
         end
       end
   
       # Determine the most specific manifest record for the given
       # segment/filename, given information in the node. Throws
       # FileNotFound if there is no such segment and filename in the
       # manifest.
       #
       # A manifest record is a Mash that follows the following form:
       # {
       #   :name => "example.rb",
       #   :path => "files/default/example.rb",
       #   :specificity => "default",
       #   :checksum => "1234"
       # }
       def preferred_manifest_record(node, segment, filename)
         preferences = preferences_for_path(node, segment, filename)
   
         # ensure that we generate the manifest, which will also generate
         # @manifest_records_by_path
         manifest
   
         # in order of prefernce, look for the filename in the manifest
         found_pref = preferences.find {|preferred_filename| @manifest_records_by_path[preferred_filename] }
         if found_pref
           @manifest_records_by_path[found_pref]
         else
           if segment == :files || segment == :templates
             error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n"
             error_locations = [
               "  #{segment}/#{node[:platform]}-#{node[:platform_version]}/#{filename}",
               "  #{segment}/#{node[:platform]}/#{filename}",
               "  #{segment}/default/#{filename}",
             ]
             error_message << error_locations.join("\n")
             existing_files = segment_filenames(segment)
             # Show the files that the cookbook does have. If the user made a typo,
             # hopefully they'll see it here.
             unless existing_files.empty?
               error_message << "\n\nThis cookbook _does_ contain: ['#{existing_files.join("','")}']"
             end
             raise Chef::Exceptions::FileNotFound, error_message
           else
             raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}"
           end
         end
       end
   
       def preferred_filename_on_disk_location(node, segment, filename, current_filepath=nil)
         manifest_record = preferred_manifest_record(node, segment, filename)
         if current_filepath && (manifest_record['checksum'] == self.class.checksum_cookbook_file(current_filepath))
           nil
         else
           file_vendor.get_filename(manifest_record['path'])
         end
       end
   
       def relative_filenames_in_preferred_directory(node, segment, dirname)
         preferences = preferences_for_path(node, segment, dirname)
         filenames_by_pref = Hash.new
         preferences.each { |pref| filenames_by_pref[pref] = Array.new }
   
         manifest[segment].each do |manifest_record|
           manifest_record_path = manifest_record[:path]
   
           # find the NON SPECIFIC filenames, but prefer them by filespecificity.
           # For example, if we have a file:
           # 'files/default/somedir/somefile.conf' we only keep
           # 'somedir/somefile.conf'. If there is also
           # 'files/$hostspecific/somedir/otherfiles' that matches the requested
           # hostname specificity, that directory will win, as it is more specific.
           #
           # This is clearly ugly b/c the use case is for remote directory, where
           # we're just going to make cookbook_files out of these and make the
           # cookbook find them by filespecificity again. but it's the shortest
           # path to "success" for now.
           if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
             specificity_dirname = $1
             non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1]
             # Record the specificity_dirname only if it's in the list of
             # valid preferences
             if filenames_by_pref[specificity_dirname]
               filenames_by_pref[specificity_dirname] << non_specific_path
             end
           end
         end
   
         best_pref = preferences.find { |pref| !filenames_by_pref[pref].empty? }
   
         raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/default/#{dirname}" unless best_pref
   
         filenames_by_pref[best_pref]
   
       end
   
       # Determine the manifest records from the most specific directory
       # for the given node. See #preferred_manifest_record for a
       # description of entries of the returned Array.
       def preferred_manifest_records_for_directory(node, segment, dirname)
         preferences = preferences_for_path(node, segment, dirname)
         records_by_pref = Hash.new
         preferences.each { |pref| records_by_pref[pref] = Array.new }
   
         manifest[segment].each do |manifest_record|
           manifest_record_path = manifest_record[:path]
   
           # extract the preference part from the path.
           if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
             # Note the specificy_dirname includes the segment and
             # dirname argument as above, which is what
             # preferences_for_path returns. It could be
             # "files/ubuntu-9.10/dirname", for example.
             specificity_dirname = $1
   
             # Record the specificity_dirname only if it's in the list of
             # valid preferences
             if records_by_pref[specificity_dirname]
               records_by_pref[specificity_dirname] << manifest_record
             end
           end
         end
   
         best_pref = preferences.find { |pref| !records_by_pref[pref].empty? }
   
         raise Chef::Exceptions::FileNotFound, "cookbook #{name} (#{version}) has no directory #{segment}/default/#{dirname}" unless best_pref
   
         records_by_pref[best_pref]
       end
   
   
       # Given a node, segment and path (filename or directory name),
       # return the priority-ordered list of preference locations to
       # look.
       def preferences_for_path(node, segment, path)
         # only files and templates can be platform-specific
         if segment.to_sym == :files || segment.to_sym == :templates
           begin
             platform, version = Chef::Platform.find_platform_and_version(node)
           rescue ArgumentError => e
             # Skip platform/version if they were not found by find_platform_and_version
             if e.message =~ /Cannot find a (?:platform|version)/
               platform = "/unknown_platform/"
               version = "/unknown_platform_version/"
             else
               raise
             end
           end
   
           fqdn = node[:fqdn]
   
           # Most specific to least specific places to find the path
           [
            File.join(segment.to_s, "host-#{fqdn}", path),
            File.join(segment.to_s, "#{platform}-#{version}", path),
            File.join(segment.to_s, platform.to_s, path),
            File.join(segment.to_s, "default", path)
           ]
         else
           [File.join(segment, path)]
         end
       end
       private :preferences_for_path
   
       def to_hash
         result = manifest.dup
         result['frozen?'] = frozen_version?
         result['chef_type'] = 'cookbook_version'
         result["_rev"] = couchdb_rev if couchdb_rev
         result.to_hash
       end
   
       def to_json(*a)
         result = self.to_hash
         result['json_class'] = self.class.name
         result.to_json(*a)
       end
   
       def self.json_create(o)
         cookbook_version = new(o["cookbook_name"])
         if o.has_key?('_rev')
           cookbook_version.couchdb_rev = o["_rev"] if o.has_key?("_rev")
           o.delete("_rev")
         end
         if o.has_key?("_id")
           cookbook_version.couchdb_id = o["_id"] if o.has_key?("_id")
           cookbook_version.index_id = cookbook_version.couchdb_id
           o.delete("_id")
         end
         # We want the Chef::Cookbook::Metadata object to always be inflated
         cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"])
         cookbook_version.manifest = o
   
         # We don't need the following step when we decide to stop supporting deprecated operators in the metadata (e.g. <<, >>)
         cookbook_version.manifest["metadata"] = JSON.parse(cookbook_version.metadata.to_json)
   
         cookbook_version.freeze_version if o["frozen?"]
         cookbook_version
       end
   
       def generate_manifest_with_urls(&url_generator)
         rendered_manifest = manifest.dup
         COOKBOOK_SEGMENTS.each do |segment|
           if rendered_manifest.has_key?(segment)
             rendered_manifest[segment].each do |manifest_record|
               url_options = { :cookbook_name => name.to_s, :cookbook_version => version, :checksum => manifest_record["checksum"] }
               manifest_record["url"] = url_generator.call(url_options)
             end
           end
         end
         rendered_manifest
       end
   
       def metadata_json_file
         File.join(root_dir, "metadata.json")
       end
   
       def metadata_rb_file
         File.join(root_dir, "metadata.rb")
       end
   
       def reload_metadata!
         if File.exists?(metadata_json_file)
           metadata.from_json(IO.read(metadata_json_file))
         end
       end
   
       ##
       # REST API
       ##
       def self.chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def chef_server_rest
         self.class.chef_server_rest
       end
   
       # Return the URL to save (PUT) this object to the server via the
       # REST api. If there is an existing document on the server and it
       # is marked frozen, a PUT will result in a 409 Conflict.
       def save_url
         "cookbooks/#{name}/#{version}"
       end
   
       # Adds the `force=true` parameter to the upload URL. This allows
       # the user to overwrite a frozen cookbook (a PUT against the
       # normal #save_url raises a 409 Conflict in this case).
       def force_save_url
         "cookbooks/#{name}/#{version}?force=true"
       end
   
       def destroy
         chef_server_rest.delete_rest("cookbooks/#{name}/#{version}")
         self
       end
   
       def self.load(name, version="_latest")
         version = "_latest" if version == "latest"
         chef_server_rest.get_rest("cookbooks/#{name}/#{version}")
       end
   
       def self.list
         chef_server_rest.get_rest('cookbooks')
       end
   
       ##
       # Given a +cookbook_name+, get a list of all versions that exist on the
       # server.
       # ===Returns
       # [String]::  Array of cookbook versions, which are strings like 'x.y.z'
       # nil::       if the cookbook doesn't exist. an error will also be logged.
       def self.available_versions(cookbook_name)
         chef_server_rest.get_rest("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map do |cb|
           cb["version"]
         end
       rescue Net::HTTPServerException => e
         if e.to_s =~ /^404/
           Chef::Log.error("Cannot find a cookbook named #{cookbook_name}")
           nil
         else
           raise
         end
       end
   
       # Get the newest version of all cookbooks
       def self.latest_cookbooks
         chef_server_rest.get_rest('cookbooks/_latest')
       end
   
       ##
       # Couchdb
       ##
   
       def self.cdb_by_name(cookbook_name, couchdb=nil)
         cdb = (couchdb || Chef::CouchDB.new)
         options = { :startkey => cookbook_name, :endkey => cookbook_name }
         rs = cdb.get_view("cookbooks", "all_with_version", options)
         rs["rows"].inject({}) { |memo, row| memo.has_key?(row["key"]) ? memo[row["key"]] << row["value"] : memo[row["key"]] = [ row["value"] ]; memo }
       end
   
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("cookbooks", DESIGN_DOCUMENT)
       end
   
       def self.cdb_list_latest(inflate=false, couchdb=nil)
         couchdb ||= Chef::CouchDB.new
         if inflate
           doc_ids = cdb_list_latest_ids.map {|i|i["id"]}
           couchdb.bulk_get(doc_ids)
         else
           results = couchdb.get_view("cookbooks", "all_latest_version", :group=>true)["rows"]
           results.inject({}) { |mapped, row| mapped[row["key"]] = row["value"]; mapped}
         end
       end
   
       def self.cdb_list_latest_ids(inflate=false, couchdb=nil)
         couchdb ||= Chef::CouchDB.new
         results = couchdb.get_view("cookbooks", "all_latest_version_by_id", :group=>true)["rows"]
         results.map { |name_and_id| name_and_id["value"]}
       end
   
       def self.cdb_list(inflate=false, couchdb=nil)
         couchdb ||= Chef::CouchDB.new
         if inflate
           couchdb.list("cookbooks", true)["rows"].collect{|r| r["value"]}
         else
           # If you modify this, please make sure the desc sorted order on the versions doesn't get broken.
           couchdb.get_view("cookbooks", "all_with_version")["rows"].inject({}) { |mapped, row| mapped[row["key"]]||=Array.new; mapped[row["key"]].push(Chef::Version.new(row["value"])); mapped[row["key"]].sort!.reverse!; mapped}
         end
       end
   
       def self.cdb_load(name, version='latest', couchdb=nil)
         cdb = couchdb || Chef::CouchDB.new
         if version == "latest" || version == "_latest"
           rs = cdb.get_view("cookbooks", "all_latest_version", :key => name, :descending => true, :group => true, :reduce => true)["rows"].first
           cdb.load("cookbook_version", "#{rs["key"]}-#{rs["value"]}")
         else
           cdb.load("cookbook_version", "#{name}-#{version}")
         end
       end
   
       def cdb_destroy
         (couchdb || Chef::CouchDB.new).delete("cookbook_version", full_name, couchdb_rev)
       end
   
       # Runs on Chef Server (API); deletes the cookbook from couchdb and also destroys associated
       # checksum documents
       def purge
         checksums.keys.each do |checksum|
           begin
             Chef::Checksum.cdb_load(checksum, couchdb).purge
           rescue Chef::Exceptions::CouchDBNotFound
           end
         end
         cdb_destroy
       end
   
       def cdb_save
         @couchdb_rev = couchdb.store("cookbook_version", full_name, self)["rev"]
       end
   
       def couchdb_id=(value)
         @couchdb_id = value
         @index_id = value
       end
   
       def <=>(o)
         raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
         # FIXME: can we change the interface to the Metadata class such
         # that metadata.version returns a Chef::Version instance instead
         # of a string?
         Chef::Version.new(self.version) <=> Chef::Version.new(o.version)
       end
   
       private
   
       # For each filename, produce a mapping of base filename (i.e. recipe name
       # or attribute file) to on disk location
       def filenames_by_name(filenames)
         filenames.select{|filename| filename =~ /\.rb$/}.inject({}){|memo, filename| memo[File.basename(filename, '.rb')] = filename ; memo }
       end
   
       # See #manifest for a description of the manifest return value.
       # See #preferred_manifest_record for a description an individual manifest record.
       def generate_manifest
         manifest = Mash.new({
           :recipes => Array.new,
           :definitions => Array.new,
           :libraries => Array.new,
           :attributes => Array.new,
           :files => Array.new,
           :templates => Array.new,
           :resources => Array.new,
           :providers => Array.new,
           :root_files => Array.new
         })
         checksums_to_on_disk_paths = {}
   
         COOKBOOK_SEGMENTS.each do |segment|
           segment_filenames(segment).each do |segment_file|
             next if File.directory?(segment_file)
   
             file_name = nil
             path = nil
             specificity = "default"
   
             if segment == :root_files
               matcher = segment_file.match(".+/#{Regexp.escape(name.to_s)}/(.+)")
               file_name = matcher[1]
               path = file_name
             elsif segment == :templates || segment == :files
               matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+?)/(.+))")
               unless matcher
                 Chef::Log.debug("Skipping file #{segment_file}, as it isn't in any of the proper directories (platform-version, platform or default)")
                 Chef::Log.debug("You probably need to move #{segment_file} into the 'default' sub-directory")
                 next
               end
               path = matcher[1]
               specificity = matcher[2]
               file_name = matcher[3]
             else
               matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+))")
               path = matcher[1]
               file_name = matcher[2]
             end
   
             csum = self.class.checksum_cookbook_file(segment_file)
             checksums_to_on_disk_paths[csum] = segment_file
             rs = Mash.new({
               :name => file_name,
               :path => path,
               :checksum => csum
             })
             rs[:specificity] = specificity
   
             manifest[segment] << rs
           end
         end
   
         manifest[:cookbook_name] = name.to_s
         manifest[:metadata] = metadata
         manifest[:version] = metadata.version
         manifest[:name] = full_name
   
         @checksums = checksums_to_on_disk_paths
         @manifest = manifest
         @manifest_records_by_path = extract_manifest_records_by_path(manifest)
       end
   
       def file_vendor
         unless @file_vendor
           @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
         end
         @file_vendor
       end
   
       def extract_checksums_from_manifest(manifest)
         checksums = {}
         COOKBOOK_SEGMENTS.each do |segment|
           next unless manifest.has_key?(segment)
           manifest[segment].each do |manifest_record|
             checksums[manifest_record[:checksum]] = nil
           end
         end
         checksums
       end
   
       def extract_manifest_records_by_path(manifest)
         manifest_records_by_path = {}
         COOKBOOK_SEGMENTS.each do |segment|
           next unless manifest.has_key?(segment)
           manifest[segment].each do |manifest_record|
             manifest_records_by_path[manifest_record[:path]] = manifest_record
           end
         end
         manifest_records_by_path
       end
   
     end
   end

lib/chef/resource/route.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Route < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :route
           @target = name
           @action = :add
           @allowed_actions.push(:add, :delete)
           @netmask = nil
           @gateway = nil
           @metric = nil
           @device = nil 
           @route_type = :host
           @networking = nil
           @networking_ipv6 = nil
           @hostname = nil
           @domainname = nil
           @domain = nil
         end
   
         def networking(arg=nil)
           set_or_return(
             :networking,
             arg,
             :kind_of => String
           )
         end
   
         def networking_ipv6(arg=nil)
           set_or_return(
             :networking_ipv6,
             arg,
             :kind_of => String
           )
         end
   
         def hostname(arg=nil)
           set_or_return(
             :hostname,
             arg,
             :kind_of => String
           )
         end
   
         def domainname(arg=nil)
           set_or_return(
             :domainname,
             arg,
             :kind_of => String
           )
         end
   
         def domain(arg=nil)
           set_or_return(
             :domain,
             arg,
             :kind_of => String
           )
         end
   
         def target(arg=nil)
           set_or_return(
             :target,
             arg,
             :kind_of => String
           )
         end
   
         def netmask(arg=nil)
           set_or_return(
             :netmask,
             arg,
             :kind_of => String
           )
         end
   
         def gateway(arg=nil)
           set_or_return(
             :gateway,
             arg,
             :kind_of => String
           )
         end
   
         def metric(arg=nil)
           set_or_return(
             :metric,
             arg,
             :kind_of => Integer
           )
         end
   
         def device(arg=nil)
           set_or_return(
             :device,
             arg,
             :kind_of => String
           )
         end
   
         def route_type(arg=nil)
           real_arg = arg.kind_of?(String) ? arg.to_sym : arg
           set_or_return(
             :route_type,
             real_arg,
             :equal_to => [ :host, :net ]
           )
         end
       end
     end
   end
   
   

lib/chef/cookbook_version_selector.rb

   #
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'dep_selector'
   
   class Chef
     module CookbookVersionSelector
       # This method replaces verbiage from DepSelector messages with
       # Chef-domain-specific verbiage, such as replacing package with
       # cookbook.
       #
       # TODO [cw, 2011/2/25]: this is a near-term hack. In the long run,
       # we'll do this better.
       def self.filter_dep_selector_message(message)
         m = message
         m.gsub!("Package", "Cookbook")
         m.gsub!("package", "cookbook")
         m.gsub!("Solution constraint", "Run list item")
         m.gsub!("solution constraint", "run list item")
         m
       end
   
       # all_cookbooks - a hash mapping cookbook names to an array of
       # available CookbookVersions.
       #
       # Creates a DependencyGraph from CookbookVersion objects
       def self.create_dependency_graph_from_cookbooks(all_cookbooks)
         dep_graph = DepSelector::DependencyGraph.new
   
         all_cookbooks.each do |cb_name, cb_versions|
           cb_versions.each do |cb_version|
             cb_version_deps = cb_version.metadata.dependencies
             # TODO [cw. 2011/2/10]: CookbookVersion#version returns a
             # String even though we're storing as a DepSelector::Version
             # object underneath. This should be changed so that we
             # return the object and handle proper serialization and
             # de-serialization. For now, I'm just going to create a
             # Version object from the String representation.
             pv = dep_graph.package(cb_name).add_version(Chef::Version.new(cb_version.version))
             cb_version_deps.each_pair do |dep_name, constraint_str|
               # if the dependency is specified as cookbook::recipe,
               # extract the cookbook component
               dep_cb_name = dep_name.split("::").first
               constraint = Chef::VersionConstraint.new(constraint_str)
               pv.dependencies << DepSelector::Dependency.new(dep_graph.package(dep_cb_name), constraint)
             end
           end
         end
   
         dep_graph
       end
   
       # Return a hash mapping cookbook names to a CookbookVersion
       # object. If there is no solution that satisfies the constraints,
       # the first run list item that caused unsatisfiability is
       # returned.
       #
       # This is the final version-resolved list of cookbooks for the
       # RunList.
       #
       # all_cookbooks - a hash mapping cookbook names to an array of
       # available CookbookVersions.
       #
       # recipe_constraints - an array of hashes describing the expanded
       # run list.  Each element is a hash containing keys :name and
       # :version_constraint. The :name component is either the
       # fully-qualified recipe name (e.g. "cookbook1::non_default_recipe")
       # or just a cookbook name, indicating the default recipe is to be
       # run (e.g. "cookbook1").
       def self.constrain(all_cookbooks, recipe_constraints)
         dep_graph = create_dependency_graph_from_cookbooks(all_cookbooks)
   
         # extract cookbook names from (possibly) fully-qualified recipe names
         cookbook_constraints = recipe_constraints.map do |recipe_spec|
           cookbook_name = (recipe_spec[:name][/^(.+)::/, 1] || recipe_spec[:name])
           DepSelector::SolutionConstraint.new(dep_graph.package(cookbook_name),
                                               recipe_spec[:version_constraint])
         end
   
         # Pass in the list of all available cookbooks (packages) so that
         # DepSelector can distinguish between "no version available for
         # cookbook X" and "no such cookbook X" when an environment
         # filters out all versions for a given cookbook.
         all_packages = all_cookbooks.inject([]) do |acc, (cookbook_name, cookbook_versions)|
           acc << dep_graph.package(cookbook_name)
           acc
         end
   
         # find a valid assignment of CoookbookVersions. If no valid
         # assignment exists, indicate which run_list_item causes the
         # unsatisfiability and try to hint at what might be wrong.
         soln =
           begin
             DepSelector::Selector.new(dep_graph).find_solution(cookbook_constraints, all_packages)
           rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
             non_existent_cookbooks = e.non_existent_packages.map {|constraint| constraint.package.name}
             cookbooks_with_no_matching_versions = e.constrained_to_no_versions.map {|constraint| constraint.package.name}
   
             # Spend a whole lot of effort for pluralizing and
             # prettifying the message.
             message = ""
             if non_existent_cookbooks.length > 0
               message += "no such " + (non_existent_cookbooks.length > 1 ? "cookbooks" : "cookbook")
               message += " #{non_existent_cookbooks.join(", ")}"
             end
   
             if cookbooks_with_no_matching_versions.length > 0
               if message.length > 0
                 message += "; "
               end
   
               message += "no versions match the constraints on " + (cookbooks_with_no_matching_versions.length > 1 ? "cookbooks" : "cookbook")
               message += " #{cookbooks_with_no_matching_versions.join(", ")}"
             end
   
             message = "Run list contains invalid items: #{message}."
   
             raise Chef::Exceptions::CookbookVersionSelection::InvalidRunListItems.new(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
           rescue DepSelector::Exceptions::NoSolutionExists => e
             raise Chef::Exceptions::CookbookVersionSelection::UnsatisfiableRunListItem.new(filter_dep_selector_message(e.message), e.unsatisfiable_solution_constraint, e.disabled_non_existent_packages, e.disabled_most_constrained_packages)
           end
   
   
         # map assignment back to CookbookVersion objects
         selected_cookbooks = {}
         soln.each_pair do |cb_name, cb_version|
           # TODO [cw, 2011/2/10]: related to the TODO in
           # create_dependency_graph_from_cookbooks, cbv.version
           # currently returns a String, so we must compare to
           # cb_version.to_s, since it's a for-real Version object.
           selected_cookbooks[cb_name] = all_cookbooks[cb_name].find{|cbv| cbv.version == cb_version.to_s}
         end
         selected_cookbooks
       end
   
       # Expands the run_list, constrained to the environment's CookbookVersion
       # constraints.
       #
       # Returns:
       #   Hash of: name to CookbookVersion
       def self.expand_to_cookbook_versions(run_list, environment, couchdb=nil)
         # expand any roles in this run_list.
         expanded_run_list = run_list.expand(environment, 'couchdb', :couchdb => couchdb).recipes.with_version_constraints
   
         cookbooks_for_environment = Chef::Environment.cdb_minimal_filtered_versions(environment, couchdb)
         cookbook_collection = constrain(cookbooks_for_environment, expanded_run_list)
         full_cookbooks = Chef::MinimalCookbookVersion.load_full_versions_of(cookbook_collection.values, couchdb)
         full_cookbooks.inject({}) do |cb_map, cookbook_version|
           cb_map[cookbook_version.name] = cookbook_version
           cb_map
         end
       end
     end
   end

lib/chef/environment.rb

   #
   # Author:: Stephen Delano ()
   # Author:: Seth Falcon ()
   # Author:: John Keiser ()
   # Copyright:: Copyright 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mash'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/couchdb'
   require 'chef/index_queue'
   require 'chef/version_constraint'
   
   class Chef
     class Environment
   
       DEFAULT = "default"
   
       include Chef::Mixin::ParamsValidate
       include Chef::Mixin::FromFile
       include Chef::IndexQueue::Indexable
   
       COMBINED_COOKBOOK_CONSTRAINT = /(.+)(?:[\s]+)((?:#{Chef::VersionConstraint::OPS.join('|')})(?:[\s]+).+)$/.freeze
   
       attr_accessor :couchdb, :couchdb_rev
       attr_reader :couchdb_id
   
       DESIGN_DOCUMENT = {
         "version" => 1,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "environment") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "environment") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           }
         }
       }
   
       def initialize(couchdb=nil)
         @name = ''
         @description = ''
         @default_attributes = Mash.new
         @override_attributes = Mash.new
         @cookbook_versions = Hash.new
         @couchdb_rev = nil
         @couchdb_id = nil
         @couchdb = couchdb || Chef::CouchDB.new
       end
   
       def couchdb_id=(value)
         @couchdb_id = value
         self.index_id = value
       end
   
       def chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def self.chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def name(arg=nil)
         set_or_return(
           :name,
           arg,
           { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String }
         )
       end
   
       def description(arg=nil)
         set_or_return(
           :description,
           arg,
           :kind_of => String
         )
       end
   
       def default_attributes(arg=nil)
         set_or_return(
           :default_attributes,
           arg,
           :kind_of => Hash
         )
       end
   
       def override_attributes(arg=nil)
         set_or_return(
           :override_attributes,
           arg,
           :kind_of => Hash
         )
       end
   
       def cookbook_versions(arg=nil)
         set_or_return(
           :cookbook_versions,
           arg,
           {
             :kind_of => Hash,
             :callbacks => {
               "should be a valid set of cookbook version requirements" => lambda { |cv| Chef::Environment.validate_cookbook_versions(cv) }
             }
           }
         )
       end
   
       def cookbook(cookbook, version)
         validate({
           :version => version
         },{
           :version => {
             :callbacks => { "should be a valid version requirement" => lambda { |v| Chef::Environment.validate_cookbook_version(v) } }
           }
         })
         @cookbook_versions[cookbook] = version
       end
   
       def to_hash
         result = {
           "name" => @name,
           "description" => @description,
           "cookbook_versions" =>  @cookbook_versions,
           "json_class" => self.class.name,
           "chef_type" => "environment",
           "default_attributes" => @default_attributes,
           "override_attributes" => @override_attributes
         }
         result["_rev"] = couchdb_rev if couchdb_rev
         result
       end
   
       def to_json(*a)
         to_hash.to_json(*a)
       end
   
       def update_from!(o)
         description(o.description)
         cookbook_versions(o.cookbook_versions)
         default_attributes(o.default_attributes)
         override_attributes(o.override_attributes)
         self
       end
   
   
       def update_attributes_from_params(params)
         unless params[:default_attributes].nil? || params[:default_attributes].size == 0
           default_attributes(Chef::JSONCompat.from_json(params[:default_attributes]))
         end
         unless params[:override_attributes].nil? || params[:override_attributes].size == 0
           override_attributes(Chef::JSONCompat.from_json(params[:override_attributes]))
         end
       end
   
       def update_from_params(params)
         # reset because everything we need will be in the params, this is necessary because certain constraints
         # may have been removed in the params and need to be removed from cookbook_versions as well.
         bkup_cb_versions = cookbook_versions
         cookbook_versions(Hash.new)
         valid = true
   
         begin
           name(params[:name])
         rescue Chef::Exceptions::ValidationFailed => e
           invalid_fields[:name] = e.message
           valid = false
         end
         description(params[:description])
   
         unless params[:cookbook_version].nil?
           params[:cookbook_version].each do |index, cookbook_constraint_spec|
             unless (cookbook_constraint_spec.nil? || cookbook_constraint_spec.size == 0)
               valid = valid && update_cookbook_constraint_from_param(index, cookbook_constraint_spec)
             end
           end
         end
   
         update_attributes_from_params(params)
   
         valid = validate_required_attrs_present && valid
         cookbook_versions(bkup_cb_versions) unless valid # restore the old cookbook_versions if valid is false
         valid
       end
   
       def update_cookbook_constraint_from_param(index, cookbook_constraint_spec)
         valid = true
         md = cookbook_constraint_spec.match(COMBINED_COOKBOOK_CONSTRAINT)
         if md.nil? || md[2].nil?
           valid = false
           add_cookbook_constraint_error(index, cookbook_constraint_spec)
         elsif self.class.validate_cookbook_version(md[2])
           cookbook_versions[md[1]] = md[2]
         else
           valid = false
           add_cookbook_constraint_error(index, cookbook_constraint_spec)
         end
         valid
       end
   
       def add_cookbook_constraint_error(index, cookbook_constraint_spec)
         invalid_fields[:cookbook_version] ||= {}
         invalid_fields[:cookbook_version][index] = "#{cookbook_constraint_spec} is not a valid cookbook constraint"
       end
   
       def invalid_fields
         @invalid_fields ||= {}
       end
   
       def validate_required_attrs_present
         if name.nil? || name.size == 0
           invalid_fields[:name] ||= "name cannot be empty"
           false
         else
           true
         end
       end
   
   
       def self.json_create(o)
         environment = new
         environment.name(o["name"])
         environment.description(o["description"])
         environment.cookbook_versions(o["cookbook_versions"])
         environment.default_attributes(o["default_attributes"])
         environment.override_attributes(o["override_attributes"])
         environment.couchdb_rev = o["_rev"] if o.has_key?("_rev")
         environment.couchdb_id = o["_id"] if o.has_key?("_id")
         environment
       end
   
       def self.cdb_list(inflate=false, couchdb=nil)
         es = (couchdb || Chef::CouchDB.new).list("environments", inflate)
         lookup = (inflate ? "value" : "key")
         es["rows"].collect { |e| e[lookup] }
       end
   
       def self.list(inflate=false)
         if inflate
           # TODO: index the environments and use search to inflate - don't inflate for now :(
           chef_server_rest.get_rest("environments")
         else
           chef_server_rest.get_rest("environments")
         end
       end
   
       def self.cdb_load(name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("environment", name)
       end
   
       def self.load(name)
         chef_server_rest.get_rest("environments/#{name}")
       end
   
       def self.exists?(name, couchdb)
         begin
           self.cdb_load(name, couchdb)
         rescue Chef::Exceptions::CouchDBNotFound
           nil
         end
       end
   
       def cdb_destroy
         couchdb.delete("environment", @name, couchdb_rev)
       end
   
       def destroy
         chef_server_rest.delete_rest("environments/#{@name}")
       end
   
       def cdb_save
         self.couchdb_rev = couchdb.store("environment", @name, self)["rev"]
       end
   
       def save
         begin
           chef_server_rest.put_rest("environments/#{@name}", self)
         rescue Net::HTTPServerException => e
           raise e unless e.response.code == "404"
           chef_server_rest.post_rest("environments", self)
         end
         self
       end
   
       def create
         chef_server_rest.post_rest("environments", self)
         self
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("environments", DESIGN_DOCUMENT)
       end
   
       # Loads the set of Chef::CookbookVersion objects available to a given environment
       # === Returns
       # Hash
       # i.e.
       # {
       #   "cookbook_name" => [ Chef::CookbookVersion ... ] ## the array of CookbookVersions is sorted highest to lowest
       # }
       #
       # There will be a key for every cookbook.  If no CookbookVersions
       # are available for the specified environment the value will be an
       # empty list.
       #
       def self.cdb_load_filtered_cookbook_versions(name, couchdb=nil)
         version_constraints = cdb_load(name, couchdb).cookbook_versions.inject({}) {|res, (k,v)| res[k] = Chef::VersionConstraint.new(v); res}
   
         # inject all cookbooks into the hash while filtering out restricted versions, then sort the individual arrays
         cookbook_list = Chef::CookbookVersion.cdb_list(true, couchdb)
   
         filtered_list = cookbook_list.inject({}) do |res, cookbook|
           # FIXME: should cookbook.version return a Chef::Version?
           version               = Chef::Version.new(cookbook.version)
           requirement_satisfied = version_constraints.has_key?(cookbook.name) ? version_constraints[cookbook.name].include?(version) : true
           # we want a key for every cookbook, even if no versions are available
           res[cookbook.name] ||= []
           res[cookbook.name] << cookbook if requirement_satisfied
           res
         end
   
         sorted_list = filtered_list.inject({}) do |res, (cookbook_name, versions)|
           res[cookbook_name] = versions.sort.reverse
           res
         end
   
         sorted_list
       end
   
       # Like +cdb_load_filtered_cookbook_versions+, loads the set of
       # cookbooks available in a given environment. The difference is that
       # this method will load Chef::MinimalCookbookVersion objects that
       # contain only the information necessary for solving a cookbook
       # collection for a given run list. The user of this method must call
       # Chef::MinimalCookbookVersion.load_full_versions_of() after solving
       # the cookbook collection to get the full objects.
       # === Returns
       # Hash
       # i.e.
       # {
       #   "cookbook_name" => [ Chef::CookbookVersion ... ] ## the array of CookbookVersions is sorted highest to lowest
       # }
       #
       # There will be a key for every cookbook.  If no CookbookVersions
       # are available for the specified environment the value will be an
       # empty list.
       def self.cdb_minimal_filtered_versions(name, couchdb=nil)
         version_constraints = cdb_load(name, couchdb).cookbook_versions.inject({}) {|res, (k,v)| res[k] = Chef::VersionConstraint.new(v); res}
   
         # inject all cookbooks into the hash while filtering out restricted versions, then sort the individual arrays
         cookbook_list = Chef::MinimalCookbookVersion.load_all(couchdb)
   
         filtered_list = cookbook_list.inject({}) do |res, cookbook|
           # FIXME: should cookbook.version return a Chef::Version?
           version               = Chef::Version.new(cookbook.version)
           requirement_satisfied = version_constraints.has_key?(cookbook.name) ? version_constraints[cookbook.name].include?(version) : true
           # we want a key for every cookbook, even if no versions are available
           res[cookbook.name] ||= []
           res[cookbook.name] << cookbook if requirement_satisfied
           res
         end
   
         sorted_list = filtered_list.inject({}) do |res, (cookbook_name, versions)|
           res[cookbook_name] = versions.sort.reverse
           res
         end
   
         sorted_list
       end
   
       def self.cdb_load_filtered_recipe_list(name, couchdb=nil)
         cdb_load_filtered_cookbook_versions(name, couchdb).map do |cb_name, cb|
           if cb.empty?            # no available versions
             []                    # empty list elided with flatten
           else
             latest_version = cb.first
             latest_version.recipe_filenames_by_name.keys.map do |recipe|
               case recipe
               when DEFAULT
                 cb_name
               else
                 "#{cb_name}::#{recipe}"
               end
             end
           end
         end.flatten
       end
   
       def self.load_filtered_recipe_list(environment)
         chef_server_rest.get_rest("environments/#{environment}/recipes")
       end
   
       def to_s
         @name
       end
   
       def self.validate_cookbook_versions(cv)
         return false unless cv.kind_of?(Hash)
         cv.each do |cookbook, version|
           return false unless Chef::Environment.validate_cookbook_version(version)
         end
         true
       end
   
       def self.validate_cookbook_version(version)
         begin
           Chef::VersionConstraint.new version
           true
         rescue ArgumentError
           false
         end
       end
   
       def self.create_default_environment(couchdb=nil)
         couchdb = couchdb || Chef::CouchDB.new
         begin
           Chef::Environment.cdb_load('_default', couchdb)
         rescue Chef::Exceptions::CouchDBNotFound
           env = Chef::Environment.new(couchdb)
           env.name '_default'
           env.description 'The default Chef environment'
           env.cdb_save
         end
       end
     end
   end

lib/chef/resource/user.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class User < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :user
           @username = name
           @comment = nil
           @uid = nil
           @gid = nil
           @home = nil
           @shell = nil
           @password = nil
           @system = false
           @manage_home = false
           @non_unique = false
           @action = :create
           @supports = { 
             :manage_home => false,
             :non_unique => false
           }
           @allowed_actions.push(:create, :remove, :modify, :manage, :lock, :unlock)
         end
         
         def username(arg=nil)
           set_or_return(
             :username,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def comment(arg=nil)
           set_or_return(
             :comment,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def uid(arg=nil)
           set_or_return(
             :uid,
             arg,
             :kind_of => [ String, Integer ]
           )
         end
         
         def gid(arg=nil)
           set_or_return(
             :gid,
             arg,
             :kind_of => [ String, Integer ]
           )
         end
         
         alias_method :group, :gid
         
         def home(arg=nil)
           set_or_return(
             :home,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def shell(arg=nil)
           set_or_return(
             :shell,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def password(arg=nil)
           set_or_return(
             :password,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def system(arg=nil)
           set_or_return(
             :system,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def manage_home(arg=nil)
           set_or_return(
             :manage_home,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def non_unique(arg=nil)
           set_or_return(
             :non_unique,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
         
       end
     end
   end

lib/chef/resource/scm.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Scm < Chef::Resource
   
         def initialize(name, run_context=nil)
           super
           @destination = name
           @resource_name = :scm
           @enable_submodules = false
           @revision = "HEAD"
           @remote = "origin"
           @ssh_wrapper = nil
           @depth = nil
           @allowed_actions.push(:checkout, :export, :sync, :diff, :log)
           @action = [:sync]
         end
   
         def destination(arg=nil)
           set_or_return(
             :destination,
             arg,
             :kind_of => String
           )
         end
   
         def repository(arg=nil)
           set_or_return(
             :repository,
             arg,
             :kind_of => String
           )
         end
   
         def revision(arg=nil)
           set_or_return(
             :revision,
             arg,
             :kind_of => String
           )
         end
   
         def user(arg=nil)
           set_or_return(
             :user,
             arg,
             :kind_of => [String, Integer]
           )
         end
   
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :kind_of => [String, Integer]
           )
         end
   
         def svn_username(arg=nil)
           set_or_return(
             :svn_username,
             arg,
             :kind_of => String
           )
         end
   
         def svn_password(arg=nil)
           set_or_return(
             :svn_password,
             arg,
             :kind_of => String
           )
         end
   
         def svn_arguments(arg=nil)
           @svn_arguments, arg = nil, nil if arg == false
           set_or_return(
             :svn_arguments,
             arg,
             :kind_of => String
           )
         end
   
         def svn_info_args(arg=nil)
           @svn_info_args, arg = nil, nil if arg == false
           set_or_return(
             :svn_info_args,
             arg,
             :kind_of => String)
         end
   
         # Capistrano and git-deploy use ``shallow clone''
         def depth(arg=nil)
           set_or_return(
             :depth,
             arg,
             :kind_of => Integer
           )
         end
   
         def enable_submodules(arg=nil)
           set_or_return(
             :enable_submodules,
             arg,
             :kind_of => [TrueClass, FalseClass]
           )
         end
   
         def remote(arg=nil)
           set_or_return(
             :remote,
             arg,
             :kind_of => String
           )
         end
   
         def ssh_wrapper(arg=nil)
           set_or_return(
             :ssh_wrapper,
             arg,
             :kind_of => String
           )
         end
   
       end
     end
   end

lib/chef/resource_collection.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   require 'chef/resource_collection/stepable_iterator'
   
   class Chef
     class ResourceCollection
       include Enumerable
       
       attr_reader :iterator
   
       def initialize
         @resources = Array.new
         @resources_by_name = Hash.new
         @insert_after_idx = nil
       end
       
       def all_resources
         @resources
       end
       
       def [](index)
         @resources[index]
       end
       
       def []=(index, arg)
         is_chef_resource(arg)
         @resources[index] = arg 
         @resources_by_name[arg.to_s] = index
       end
   
       def <<(*args)
         args.flatten.each do |a|
           is_chef_resource(a)
           @resources << a
           @resources_by_name[a.to_s] = @resources.length - 1 
         end
       end
   
       def insert(resource)
         is_chef_resource(resource)
         if @insert_after_idx
           # in the middle of executing a run, so any resources inserted now should
           # be placed after the most recent addition done by the currently executing
           # resource
           @resources.insert(@insert_after_idx + 1, resource)
           # update name -> location mappings and register new resource
           @resources_by_name.each_key do |key|
             @resources_by_name[key] += 1 if @resources_by_name[key] > @insert_after_idx
           end
           @resources_by_name[resource.to_s] = @insert_after_idx + 1
           @insert_after_idx += 1
         else  
           @resources << resource
           @resources_by_name[resource.to_s] = @resources.length - 1
         end
       end
       
       def push(*args)
         args.flatten.each do |arg|
           is_chef_resource(arg)
           @resources.push(arg)
           @resources_by_name[arg.to_s] = @resources.length - 1
         end
       end
     
       def each
         @resources.each do |resource|
           yield resource
         end
       end
   
       def execute_each_resource(&resource_exec_block)
         @iterator = StepableIterator.for_collection(@resources)
         @iterator.each_with_index do |resource, idx|
           @insert_after_idx = idx
           yield resource
         end
       end
       
       def each_index
         @resources.each_index do |i|
           yield i
         end
       end
       
       def lookup(resource)
         lookup_by = nil
         if resource.kind_of?(Chef::Resource)
           lookup_by = resource.to_s
         elsif resource.kind_of?(String)
           lookup_by = resource
         else
           raise ArgumentError, "Must pass a Chef::Resource or String to lookup"
         end
         res = @resources_by_name[lookup_by]
         unless res
           raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{lookup_by} (did you define it first?)"
         end
         @resources[res]
       end
   
       # Find existing resources by searching the list of existing resources.  Possible
       # forms are:
       #
       # find(:file => "foobar")
       # find(:file => [ "foobar", "baz" ])
       # find("file[foobar]", "file[baz]")
       # find("file[foobar,baz]")
       #
       # Returns the matching resource, or an Array of matching resources. 
       #
       # Raises an ArgumentError if you feed it bad lookup information
       # Raises a Runtime Error if it can't find the resources you are looking for.
       def find(*args)
         results = Array.new
         args.each do |arg|
           case arg
           when Hash
             results << find_resource_by_hash(arg)
           when String
             results << find_resource_by_string(arg)
           else
             msg = "arguments to #{self.class.name}#find should be of the form :resource => 'name' or resource[name]"
             raise Chef::Exceptions::InvalidResourceSpecification, msg
           end
         end
         flat_results = results.flatten
         flat_results.length == 1 ? flat_results[0] : flat_results
       end
       
       # resources is a poorly named, but we have to maintain it for back
       # compat.
       alias_method :resources, :find
       
       # Serialize this object as a hash 
       def to_json(*a)
         instance_vars = Hash.new
         self.instance_variables.each do |iv|
           instance_vars[iv] = self.instance_variable_get(iv)
         end
         results = {
           'json_class' => self.class.name,
           'instance_vars' => instance_vars
         }
         results.to_json(*a)
       end
       
       def self.json_create(o)
         collection = self.new()
         o["instance_vars"].each do |k,v|
           collection.instance_variable_set(k.to_sym, v)
         end
         collection
       end
   
       private
       
         def find_resource_by_hash(arg)
           results = Array.new
           arg.each do |resource_name, name_list|
             names = name_list.kind_of?(Array) ? name_list : [ name_list ]
             names.each do |name|
               res_name = "#{resource_name.to_s}[#{name}]"
               results << lookup(res_name)
             end
           end
           return results
         end
   
         def find_resource_by_string(arg)
           results = Array.new
           case arg
           when /^(.+)\[(.+?),(.+)\]$/
             resource_type = $1
             arg =~ /^.+\[(.+)\]$/
             resource_list = $1
             resource_list.split(",").each do |name|
               resource_name = "#{resource_type}[#{name}]" 
               results << lookup(resource_name)
             end
           when /^(.+)\[(.+)\]$/
             resource_type = $1
             name = $2
             resource_name = "#{resource_type}[#{name}]"
             results << lookup(resource_name)
           else
             raise ArgumentError, "You must have a string like resource_type[name]!"
           end
           return results
         end
   
         def is_chef_resource(arg)
           unless arg.kind_of?(Chef::Resource)
             raise ArgumentError, "Members must be Chef::Resource's" 
           end
           true
         end
     end
   end

lib/chef/provider/user/pw.rb

   #
   # Author:: Stephen Haynes ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/user'
   
   class Chef
     class Provider
       class User
         class Pw < Chef::Provider::User
   
           def load_current_resource
             super
             raise Chef::Exceptions::User, "Could not find binary /usr/sbin/pw for #{@new_resource}" unless ::File.exists?("/usr/sbin/pw")
           end
   
           def create_user
             command = "pw useradd"
             command << set_options
             run_command(:command => command)
             modify_password
           end
           
           def manage_user
             command = "pw usermod"
             command << set_options
             run_command(:command => command)
             modify_password
           end
           
           def remove_user
             command = "pw userdel #{@new_resource.username}"
             command << " -r" if @new_resource.supports[:manage_home]
             run_command(:command => command)
           end
           
           def check_lock
             case @current_resource.password
             when /^\*LOCKED\*/
               @locked = true
             else
               @locked = false
             end
             @locked
           end
           
           def lock_user
             run_command(:command => "pw lock #{@new_resource.username}")
           end
           
           def unlock_user
             run_command(:command => "pw unlock #{@new_resource.username}")
           end
           
           def set_options
             opts = " #{@new_resource.username}"
             
             field_list = {
               'comment' => "-c",
               'home' => "-d",
               'gid' => "-g",
               'uid' => "-u",
               'shell' => "-s"
             }
             field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option|
               field_symbol = field.to_sym
               if @current_resource.send(field_symbol) != @new_resource.send(field_symbol)
                 if @new_resource.send(field_symbol)
                   Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}")
                   opts << " #{option} '#{@new_resource.send(field_symbol)}'"
                 end
               end
             end
             if @new_resource.supports[:manage_home]
               Chef::Log.debug("#{@new_resource} is managing the users home directory")
               opts << " -m"
             end
             opts
           end
         
           def modify_password
             if @current_resource.password != @new_resource.password
               Chef::Log.debug("#{new_resource} updating password")
               command = "pw usermod #{@new_resource.username} -H 0"
               status = popen4(command, :waitlast => true) do |pid, stdin, stdout, stderr|
                 stdin.puts "#{@new_resource.password}"
               end
               
               unless status.exitstatus == 0
                 raise Chef::Exceptions::User, "pw failed - #{status.inspect}!"
               end
             else
               Chef::Log.debug("#{new_resource} no change needed to password")
             end
           end
         end
       end
     end
   end

lib/chef/provider/package/easy_install.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/mixin/shell_out'
   require 'chef/resource/package'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Provider
       class Package
         class EasyInstall < Chef::Provider::Package
   
           include Chef::Mixin::ShellOut
   
           def install_check(name)
             check = false
   
             begin
               # first check to see if we can import it
               output = shell_out!("#{python_binary_path} -c \"import #{name}\"", :returns=>[0,1]).stderr
               if output.include? "ImportError"
                 # then check to see if its on the path
                 output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
                 if output.downcase.include? "#{name.downcase}"
                   check = true
                 end
               else
                 check = true
               end
             rescue
               # it's probably not installed
             end
   
             check
           end
   
           def easy_install_binary_path
             path = @new_resource.easy_install_binary
             path ? path : 'easy_install'
           end
   
           def python_binary_path
             path = @new_resource.python_binary
             path ? path : 'python'
           end
   
           def module_name
             m = @new_resource.module_name
             m ? m : @new_resource.name
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             @current_resource.version(nil)
   
             # get the currently installed version if installed
             package_version = nil
             if install_check(module_name)
               begin
                 output = shell_out!("#{python_binary_path} -c \"import #{module_name}; print #{module_name}.__version__\"").stdout
                 package_version = output.strip
               rescue
                 output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
   
                 output_array = output.gsub(/[\[\]]/,'').split(/\s*,\s*/)
                 package_path = ""
   
                 output_array.each do |entry|
                   if entry.downcase.include?(@new_resource.package_name)
                     package_path = entry
                   end
                 end
   
                 package_path[/\S\S(.*)\/(.*)-(.*)-py(.*).egg\S/]
                 package_version = $3
               end
             end
   
             if package_version == @new_resource.version
               Chef::Log.debug("#{@new_resource} at version #{@new_resource.version}")
               @current_resource.version(@new_resource.version)
             else
               Chef::Log.debug("#{@new_resource} at version #{package_version}")
               @current_resource.version(package_version)
             end
   
             @current_resource
           end
   
           def candidate_version
              return @candidate_version if @candidate_version
   
              # do a dry run to get the latest version
              result = shell_out!("#{easy_install_binary_path} -n #{@new_resource.package_name}", :returns=>[0,1])
              @candidate_version = result.stdout[/(.*)Best match: (.*) (.*)$/, 3]
              @candidate_version
           end
   
           def install_package(name, version)
             run_command(:command => "#{easy_install_binary_path}#{expand_options(@new_resource.options)} \"#{name}==#{version}\"")
           end
   
           def upgrade_package(name, version)
             install_package(name, version)
           end
   
           def remove_package(name, version)
             run_command(:command => "#{easy_install_binary_path }#{expand_options(@new_resource.options)} -m #{name}")
           end
   
           def purge_package(name, version)
             remove_package(name, version)
           end
   
         end
       end
     end
   end

lib/chef/provider/http_request.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'tempfile'
   
   class Chef
     class Provider
       class HttpRequest < Chef::Provider
         
         attr_accessor :rest
         
         def load_current_resource
           @rest = Chef::REST.new(@new_resource.url, nil, nil)
         end
   
         # Send a HEAD request to @new_resource.url, with ?message=@new_resource.message
         def action_head
           message = check_message(@new_resource.message)
           modified = @rest.run_request(
             :HEAD,
             @rest.create_url("#{@new_resource.url}?message=#{message}"),
             @new_resource.headers,
             false,
             10,
             false
           )
           @new_resource.updated_by_last_action(modified)
           Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful")
           Chef::Log.debug("#{@new_resource} HEAD request response: #{modified}")
         end
   
         # Send a GET request to @new_resource.url, with ?message=@new_resource.message
         def action_get  
           message = check_message(@new_resource.message)
           body = @rest.run_request(
             :GET, 
             @rest.create_url("#{@new_resource.url}?message=#{message}"),
             @new_resource.headers,
             false,
             10,
             false
           )
           @new_resource.updated_by_last_action(true)
           Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful")
           Chef::Log.debug("#{@new_resource} GET request response: #{body}")
         end
         
         # Send a PUT request to @new_resource.url, with the message as the payload
         def action_put 
           message = check_message(@new_resource.message)
           body = @rest.run_request(
             :PUT,
             @rest.create_url("#{@new_resource.url}"),
             @new_resource.headers,
             message,
             10,
             false
           )
           @new_resource.updated_by_last_action(true)
           Chef::Log.info("#{@new_resource} PUT to #{@new_resource.url} successful")
           Chef::Log.debug("#{@new_resource} PUT request response: #{body}")
         end
         
         # Send a POST request to @new_resource.url, with the message as the payload
         def action_post
           message = check_message(@new_resource.message)
           body = @rest.run_request(
             :POST,
             @rest.create_url("#{@new_resource.url}"),
             @new_resource.headers,
             message,
             10,
             false
           )
           @new_resource.updated_by_last_action(true)
           Chef::Log.info("#{@new_resource} POST to #{@new_resource.url} message: #{message.inspect} successful")
           Chef::Log.debug("#{@new_resource} POST request response: #{body}")
         end
         
         # Send a DELETE request to @new_resource.url
         def action_delete
           body = @rest.run_request(
             :DELETE,
             @rest.create_url("#{@new_resource.url}"),
             @new_resource.headers,
             false,
             10,
             false
           )
           @new_resource.updated_by_last_action(true)
           Chef::Log.info("#{@new_resource} DELETE to #{@new_resource.url} successful")
           Chef::Log.debug("#{@new_resource} DELETE request response: #{body}")
         end
         
         private
           
           def check_message(message)
             if message.kind_of?(Proc)
               message.call
             else
               message
             end
           end
         
       end
     end
   end

lib/chef/resource/remote_directory.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/directory'
   
   class Chef
     class Resource
       class RemoteDirectory < Chef::Resource::Directory
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :remote_directory
           @path = name
           @source = ::File.basename(name)
           @delete = false
           @action = :create
           @recursive = true
           @purge = false
           @files_backup = 5
           @files_owner = nil
           @files_group = nil
           @files_mode = 0644
           @overwrite = true
           @allowed_actions.push(:create, :create_if_missing, :delete)
           @cookbook = nil
         end
   
         def source(args=nil)
           set_or_return(
             :source,
             args,
             :kind_of => String
           )
         end
   
         def files_backup(arg=nil)
           set_or_return(
             :files_backup,
             arg,
             :kind_of => [ Integer, FalseClass ]
           )
         end
   
         def purge(arg=nil)
           set_or_return(
             :purge,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def files_group(arg=nil)
           set_or_return(
             :files_group,
             arg,
             :regex => Chef::Config[:group_valid_regex]
           )
         end
   
         def files_mode(arg=nil)
           set_or_return(
             :files_mode,
             arg,
             :regex => /^\d{3,4}$/
           )
         end
   
         def files_owner(arg=nil)
           set_or_return(
             :files_owner,
             arg,
             :regex => Chef::Config[:user_valid_regex]
           )
         end
   
         def overwrite(arg=nil)
           set_or_return(
             :overwrite,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def cookbook(args=nil)
           set_or_return(
             :cookbook,
             args,
             :kind_of => String
           )
         end
   
       end
     end
   end

lib/chef/knife/status.rb

   #
   # Author:: Ian Meyer ()
   # Copyright:: Copyright (c) 2010 Ian Meyer
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class Status < Knife
   
         deps do
           require 'highline'
           require 'chef/search/query'
         end
   
         banner "knife status QUERY (options)"
   
         option :run_list,
           :short => "-r",
           :long => "--run-list",
           :description => "Show the run list"
   
         def highline
           @h ||= HighLine.new
         end
   
         def run
           all_nodes = []
           q = Chef::Search::Query.new
           query = @name_args[0] || "*:*"
           q.search(:node, query) do |node|
             all_nodes << node
           end
           all_nodes.sort { |n1, n2| (n1["ohai_time"] or 0) <=> (n2["ohai_time"] or 0) }.each do |node|
             if node.has_key?("ec2")
               fqdn = node['ec2']['public_hostname']
               ipaddress = node['ec2']['public_ipv4']
             else
               fqdn = node['fqdn']
               ipaddress = node['ipaddress']
             end
             hours, minutes, seconds = time_difference_in_hms(node["ohai_time"])
             hours_text   = "#{hours} hour#{hours == 1 ? ' ' : 's'}"
             minutes_text = "#{minutes} minute#{minutes == 1 ? ' ' : 's'}"
             run_list = ", #{node.run_list}." if config[:run_list]
             if hours > 24
               color = "RED"
               text = hours_text
             elsif hours >= 1
               color = "YELLOW"
               text = hours_text
             else
               color = "GREEN"
               text = minutes_text
             end
   
             line_parts = Array.new
             line_parts << "<%= color('#{text}', #{color}) %> ago" << node.name
             line_parts << fqdn if fqdn
             line_parts << ipaddress if ipaddress
             line_parts << run_list if run_list
   
             if node['platform']
               platform = node['platform']
               if node['platform_version']
                 platform << " #{node['platform_version']}"
               end
               line_parts << platform
             end
   
             highline.say(line_parts.join(', ') + '.')
           end
   
         end
   
         # :nodoc:
         # TODO: this is duplicated from StatusHelper in the Webui. dedup.
         def time_difference_in_hms(unix_time)
           now = Time.now.to_i
           difference = now - unix_time.to_i
           hours = (difference / 3600).to_i
           difference = difference % 3600
           minutes = (difference / 60).to_i
           seconds = (difference % 60)
           return [hours, minutes, seconds]
         end
   
       end
     end
   end

lib/chef/knife/cookbook_upload.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookUpload < Knife
   
         CHECKSUM = "checksum"
         MATCH_CHECKSUM = /[0-9a-f]{32,}/
   
         deps do
           require 'chef/exceptions'
           require 'chef/cookbook_loader'
           require 'chef/cookbook_uploader'
         end
   
         banner "knife cookbook upload [COOKBOOKS...] (options)"
   
         option :cookbook_path,
           :short => "-o PATH:PATH",
           :long => "--cookbook-path PATH:PATH",
           :description => "A colon-separated path to look for cookbooks in",
           :proc => lambda { |o| o.split(":") }
   
         option :freeze,
           :long => '--freeze',
           :description => 'Freeze this version of the cookbook so that it cannot be overwritten',
           :boolean => true
   
         option :all,
           :short => "-a",
           :long => "--all",
           :description => "Upload all cookbooks, rather than just a single cookbook"
   
         option :force,
           :long => '--force',
           :boolean => true,
           :description => "Update cookbook versions even if they have been frozen"
   
         option :environment,
           :short => '-E',
           :long  => '--environment ENVIRONMENT',
           :description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
           :default => nil
   
         option :depends,
           :short => "-d",
           :long => "--include-dependencies",
           :description => "Also upload cookbook dependencies"
   
         def run
           config[:cookbook_path] ||= Chef::Config[:cookbook_path]
   
           assert_environment_valid!
           warn_about_cookbook_shadowing
           version_constraints_to_update = {}
           # Get a list of cookbooks and their versions from the server
           # for checking existence of dependending cookbooks.
           @server_side_cookbooks = Chef::CookbookVersion.list
   
           if config[:all]
             justify_width = cookbook_repo.cookbook_names.map {|name| name.size}.max.to_i + 2
             cookbook_repo.each do |cookbook_name, cookbook|
               cookbook.freeze_version if config[:freeze]
               upload(cookbook, justify_width)
               version_constraints_to_update[cookbook_name] = cookbook.version
             end
           else
             if @name_args.empty?
               show_usage
               ui.error("You must specify the --all flag or at least one cookbook name")
               exit 1
             end
             justify_width = @name_args.map {|name| name.size }.max.to_i + 2
             @name_args.each do |cookbook_name|
               begin
                 cookbook = cookbook_repo[cookbook_name]
                 if config[:depends]
                   cookbook.metadata.dependencies.each do |dep, versions|
                     @name_args.push dep
                   end
                 end
                 cookbook.freeze_version if config[:freeze]
                 upload(cookbook, justify_width)
                 version_constraints_to_update[cookbook_name] = cookbook.version
               rescue Exceptions::CookbookNotFoundInRepo => e
                 ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
                 Log.debug(e)
               end
             end
           end
   
           ui.info "upload complete"
           update_version_constraints(version_constraints_to_update) if config[:environment]
         end
   
         def cookbook_repo
           @cookbook_loader ||= begin
             Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, config[:cookbook_path]) }
             Chef::CookbookLoader.new(config[:cookbook_path])
           end
         end
   
         def update_version_constraints(new_version_constraints)
           new_version_constraints.each do |cookbook_name, version|
             environment.cookbook_versions[cookbook_name] = "= #{version}"
           end
           environment.save
         end
   
   
         def environment
           @environment ||= config[:environment] ? Environment.load(config[:environment]) : nil
         end
   
         def warn_about_cookbook_shadowing
           unless cookbook_repo.merged_cookbooks.empty?
             ui.warn "* " * 40
             ui.warn(<<-WARNING)
   The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path.
   A composite version of these cookbooks has been compiled for uploading.
   
   #{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer
   be able to have the same version of a cookbook in multiple places in your cookbook_path.
   WARNING
             ui.warn "The affected cookbooks are located:"
             ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths)
             ui.warn "* " * 40
           end
         end
   
         private
   
         def assert_environment_valid!
           environment
         rescue Net::HTTPServerException => e
           if e.response.code.to_s == "404"
             ui.error "The environment #{config[:environment]} does not exist on the server, aborting."
             Log.debug(e)
             exit 1
           else
             raise
           end
         end
   
         def upload(cookbook, justify_width)
           ui.info("Uploading #{cookbook.name.to_s.ljust(justify_width + 10)} [#{cookbook.version}]")
   
           check_for_broken_links(cookbook)
           check_dependencies(cookbook)
           Chef::CookbookUploader.new(cookbook, config[:cookbook_path], :force => config[:force]).upload_cookbook
         rescue Net::HTTPServerException => e
           case e.response.code
           when "409"
             ui.error "Version #{cookbook.version} of cookbook #{cookbook.name} is frozen. Use --force to override."
             Log.debug(e)
           else
             raise
           end
         end
   
         # if only you people wouldn't put broken symlinks in your cookbooks in
         # the first place. ;)
         def check_for_broken_links(cookbook)
           # MUST   dup the cookbook version object--it memoizes its
           # manifest object, but the manifest becomes invalid when you
           # regenerate the metadata
           broken_files = cookbook.dup.manifest_records_by_path.select do |path, info|
             info[CHECKSUM].nil? || info[CHECKSUM] !~ MATCH_CHECKSUM
           end
           unless broken_files.empty?
             broken_filenames = Array(broken_files).map {|path, info| path}
             ui.error "The cookbook #{cookbook.name} has one or more broken files"
             ui.info "This is probably caused by broken symlinks in the cookbook directory"
             ui.info "The broken file(s) are: #{broken_filenames.join(' ')}"
             exit 1
           end
         end
   
         def check_dependencies(cookbook)
           # for each dependency, check if the version is on the server, or
           # the version is in the cookbooks being uploaded. If not, exit and warn the user.
           cookbook.metadata.dependencies.each do |cookbook_name, version|
             unless check_server_side_cookbooks(cookbook_name, version) || check_uploading_cookbooks(cookbook_name, version)
               # warn the user and exit
               ui.error "Cookbook #{cookbook.name} depends on cookbook #{cookbook_name} version #{version},"
               ui.error "which is not currently being uploaded and cannot be found on the server."
               exit 1
             end
           end
         end
   
         def check_server_side_cookbooks(cookbook_name, version)
           if @server_side_cookbooks[cookbook_name].nil?
             false
           else
             @server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
               return true if Chef::VersionConstraint.new(version).include?(versions_hash["version"])
             end
             false
           end
         end
   
         def check_uploading_cookbooks(cookbook_name, version)
           if config[:all]
             # check from all local cookbooks in the path
             unless cookbook_repo[cookbook_name].nil?
               return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
             end
           else
             # check from only those in the command argument
             if @name_args.include?(cookbook_name)
               return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
             end
           end
           false
         end
   
       end
     end
   end

lib/chef/provider/route.rb

   #
   # Author:: Bryan McLellan (btm@loftninjas.org), Jesse Nelson (spheromak@gmail.com)
   # Copyright:: Copyright (c) 2009 Bryan McLellan
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   require 'ipaddr'
   
   class Chef::Provider::Route < Chef::Provider
       include Chef::Mixin::Command
   
       attr_accessor :is_running
   
       MASK = {'0.0.0.0'          => '0',
               '128.0.0.0'        => '1',
               '192.0.0.0'        => '2',
               '224.0.0.0'        => '3',
               '240.0.0.0'        => '4',
               '248.0.0.0'        => '5',
               '252.0.0.0'        => '6',
               '254.0.0.0'        => '7',
               '255.0.0.0'        => '8',
               '255.128.0.0'      => '9',
               '255.192.0.0'      => '10',
               '255.224.0.0'      => '11',
               '255.240.0.0'      => '12',
               '255.248.0.0'      => '13',
               '255.252.0.0'      => '14',
               '255.254.0.0'      => '15',
               '255.255.0.0'      => '16',
               '255.255.128.0'    => '17',
               '255.255.192.0'    => '18',
               '255.255.224.0'    => '19',
               '255.255.240.0'    => '20',
               '255.255.248.0'    => '21',
               '255.255.252.0'    => '22',
               '255.255.254.0'    => '23',
               '255.255.255.0'    => '24',
               '255.255.255.128'  => '25',
               '255.255.255.192'  => '26',
               '255.255.255.224'  => '27',
               '255.255.255.240'  => '28',
               '255.255.255.248'  => '29',
               '255.255.255.252'  => '30',
               '255.255.255.254'  => '31',
               '255.255.255.255'  => '32' }
   
       def load_current_resource
         is_running = nil
   
         # cidr or quad dot mask
         if @new_resource.netmask
           new_ip = IPAddr.new("#{@new_resource.target}/#{@new_resource.netmask}")
         else
           new_ip = IPAddr.new(@new_resource.target)
         end
   
         # pull routes from proc
         if node[:os] == "linux"
           route_file = ::File.open("/proc/net/route", "r")
           while (line = route_file.gets)
             # proc layout
             iface,destination,gateway,flags,refcnt,use,metric,mask,mtu,window,irtt = line.split
   
             # need to convert packed adresses int quad dot
             #  the addrs are reversed hex packed decimal addrs. so this unwraps them. tho you could
             #  do this without ipaddr using unpack. ipaddr has no htoa method.
             #
             destination = IPAddr.new(destination.scan(/../).reverse.to_s.hex, Socket::AF_INET).to_s
             gateway = IPAddr.new(gateway.scan(/../).reverse.to_s.hex, Socket::AF_INET).to_s
             mask = IPAddr.new(mask.scan(/../).reverse.to_s.hex, Socket::AF_INET).to_s
             Chef::Log.debug("#{@new_resource} system has route: dest=#{destination} mask=#{mask} gw=#{gateway}")
   
             # check if what were trying to configure is already there
             # use an ipaddr object with ip/mask this way we can have
             # a new resource be in cidr format (i don't feel like
             # expanding bitmask by hand.
             #
             running_ip = IPAddr.new("#{destination}/#{mask}")
             Chef::Log.debug("#{@new_resource} new ip: #{new_ip.inspect} running ip: #{running_ip.inspect}")
             is_running = true if running_ip == new_ip
           end
         route_file.close
         end
       end
   
       def action_add
         # check to see if load_current_resource found the route
         if is_running
           Chef::Log.debug("#{@new_resource} route already active - nothing to do")
         else
           command = generate_command(:add)
   
           run_command( :command => command )
           Chef::Log.info("#{@new_resource} added")
           @new_resource.updated_by_last_action(true)
         end
   
         #for now we always write the file (ugly but its what it is)
         generate_config
       end
   
       def action_delete
         if is_running
           command = generate_command(:delete)
   
           run_command( :command => command )
           Chef::Log.info("#{@new_resource} removed")
           @new_resource.updated_by_last_action(true)
         else
           Chef::Log.debug("#{@new_resource} route does not exist - nothing to do")
         end
       end
   
       def generate_config
         conf = Hash.new
         case node[:platform]
         when ("centos" || "redhat" || "fedora")
           # walk the collection
           run_context.resource_collection.each do |resource|
             if resource.is_a? Chef::Resource::Route
               # default to eth0
               if resource.device
                 dev = resource.device
               else
                 dev = "eth0"
               end
   
               conf[dev] = String.new if conf[dev].nil?
               if resource.action == :add
                 conf[dev] = config_file_contents(:add, :target => resource.target, :netmask => resource.netmask, :gateway => resource.gateway)
               else
                 # need to do this for the case when the last route on an int
                 # is removed
                 conf[dev] = config_file_contents(:delete)
               end
             end
           end
           conf.each do |k, v|
             network_file = ::File.new("/etc/sysconfig/network-scripts/route-#{k}", "w")
             network_file.puts(conf[k])
             Chef::Log.debug("#{@new_resource} writing route.#{k}\n#{conf[k]}")
             network_file.close
           end
         end
       end
   
       def generate_command(action)
         common_route_items = ''
         common_route_items << "/#{MASK[@new_resource.netmask.to_s]}" if @new_resource.netmask
         common_route_items << " via #{@new_resource.gateway} " if @new_resource.gateway
   
         case action
         when :add
           command = "ip route replace #{@new_resource.target}"
           command << common_route_items
           command << " dev #{@new_resource.device} " if @new_resource.device
         when :delete
           command = "ip route delete #{@new_resource.target}"
           command << common_route_items
         end
   
         return command
       end
   
       def config_file_contents(action, options={})
         content = ''
         case action
         when :add
           content << "#{options[:target]}"
           content << "/#{options[:netmask]}" if options[:netmask]
           content << " via #{options[:gateway]}" if options[:gateway]
           content << "\n"
         end
   
         return content
       end
   end

lib/chef/provider/package/pacman.rb

   #
   # Author:: Jan Zimmek ()
   # Copyright:: Copyright (c) 2010 Jan Zimmek
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   
   class Chef
     class Provider
       class Package
         class Pacman < Chef::Provider::Package
         
           def load_current_resource
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
   
             @current_resource.version(nil)
   
             Chef::Log.debug("#{@new_resource} checking pacman for #{@new_resource.package_name}")
             status = popen4("pacman -Qi #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                 when /^Version(\s?)*: (.+)$/
                   Chef::Log.debug("#{@new_resource} current version is #{$2}")
                   @current_resource.version($2)
                 end
               end
             end
   
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!"
             end
   
             @current_resource
           end
   
           def candidate_version
             return @candidate_version if @candidate_version
   
             status = popen4("pacman -Ss #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                   when /^(extra|core|community)\/#{Regexp.escape(@new_resource.package_name)} (.+)$/
                     # $2 contains a string like "4.4.0-1 (kde kdenetwork)" or "3.10-4 (base)"
                     # simply split by space and use first token
                     @candidate_version = $2.split(" ").first
                 end
               end
             end
   
             unless status.exitstatus == 0 || status.exitstatus == 1
               raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!"
             end
   
             unless @candidate_version
               raise Chef::Exceptions::Package, "pacman does not have a version of package #{@new_resource.package_name}"
             end
   
             @candidate_version
   
           end
           
           def install_package(name, version)
             run_command_with_systems_locale(
               :command => "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}"
             )
           end
         
           def upgrade_package(name, version)
             install_package(name, version)
           end
         
           def remove_package(name, version)
             run_command_with_systems_locale(
               :command => "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}"
             )
           end
         
           def purge_package(name, version)
             remove_package(name, version)
           end
         
         end
       end
     end
   end

lib/chef/knife/core/cookbook_scm_repo.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/shell_out'
   
   class Chef
     class Knife
       class CookbookSCMRepo
   
         DIRTY_REPO = /^[\s]+M/
   
         include Chef::Mixin::ShellOut
   
         attr_reader :repo_path
         attr_reader :default_branch
         attr_reader :ui
   
         def initialize(repo_path, ui, opts={})
           @repo_path = repo_path
           @ui = ui
           @default_branch = 'master'
           apply_opts(opts)
         end
   
         def sanity_check
           unless ::File.directory?(repo_path)
             ui.error("The cookbook repo path #{repo_path} does not exist or is not a directory")
             exit 1
           end
           unless git_repo?(repo_path)
             ui.error "The cookbook repo #{repo_path} is not a git repository."
             ui.info("Use `git init` to initialize a git repo")
             exit 1
           end
           unless branch_exists?(default_branch)
             ui.error "The default branch '#{default_branch}' does not exist"
             ui.info "If this is a new git repo, make sure you have at least one commit before installing cookbooks"
             exit 1
           end
           cmd = git('status --porcelain')
           if cmd.stdout =~ DIRTY_REPO
             ui.error "You have uncommitted changes to your cookbook repo (#{repo_path}):"
             ui.msg cmd.stdout
             ui.info "Commit or stash your changes before importing cookbooks"
             exit 1
           end
           # TODO: any untracked files in the cookbook directory will get nuked later
           # make this an error condition also.
           true
         end
   
         def reset_to_default_state
           ui.info("Checking out the #{default_branch} branch.")
           git("checkout #{default_branch}")
         end
   
         def prepare_to_import(cookbook_name)
           branch = "chef-vendor-#{cookbook_name}"
           if branch_exists?(branch)
             ui.info("Pristine copy branch (#{branch}) exists, switching to it.")
             git("checkout #{branch}")
           else
             ui.info("Creating pristine copy branch #{branch}")
             git("checkout -b #{branch}")
           end
         end
   
         def finalize_updates_to(cookbook_name, version)
           if update_count = updated?(cookbook_name)
             ui.info "#{update_count} files updated, committing changes"
             git("add #{cookbook_name}")
             git("commit -m 'Import #{cookbook_name} version #{version}' -- #{cookbook_name}")
             ui.info("Creating tag cookbook-site-imported-#{cookbook_name}-#{version}")
             git("tag -f cookbook-site-imported-#{cookbook_name}-#{version}")
             true
           else
             ui.info("No changes made to #{cookbook_name}")
             false
           end
         end
   
         def merge_updates_from(cookbook_name, version)
           branch = "chef-vendor-#{cookbook_name}"
           Dir.chdir(repo_path) do
             if system("git merge #{branch}")
               ui.info("Cookbook #{cookbook_name} version #{version} successfully installed")
             else
               ui.error("You have merge conflicts - please resolve manually")
               ui.info("Merge status (cd #{repo_path}; git status):")
               system("git status")
               exit 3
             end
           end
         end
   
         def updated?(cookbook_name)
           update_count = git("status --porcelain -- #{cookbook_name}").stdout.strip.lines.count
           update_count == 0 ? nil : update_count
         end
   
         def branch_exists?(branch_name)
           git("branch --no-color").stdout.lines.any? {|l| l =~ /\s#{Regexp.escape(branch_name)}(?:\s|$)/ }
         end
   
         private
   
         def git_repo?(directory)
           if File.directory?(File.join(directory, '.git'))
             return true
           elsif File.dirname(directory) == directory
             return false
           else
             git_repo?(File.dirname(directory))
           end
         end
   
         def apply_opts(opts)
           opts.each do |option, value|
             case option.to_s
             when 'default_branch'
               @default_branch = value
             end
           end
         end
   
         def git(command)
           shell_out!("git #{command}", :cwd => repo_path)
         end
   
       end
     end
   end
   

lib/chef/mixin/params_validate.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     
     module Mixin
       module ParamsValidate
         
         # Takes a hash of options, along with a map to validate them.  Returns the original
         # options hash, plus any changes that might have been made (through things like setting
         # default values in the validation map)
         #
         # For example:
         #
         #   validate({ :one => "neat" }, { :one => { :kind_of => String }})
         # 
         # Would raise an exception if the value of :one above is not a kind_of? string.  Valid
         # map options are:
         #
         # :default:: Sets the default value for this parameter.
         # :callbacks:: Takes a hash of Procs, which should return true if the argument is valid.  
         #              The key will be inserted into the error message if the Proc does not return true:
         #                 "Option #{key}'s value #{value} #{message}!"
         # :kind_of:: Ensure that the value is a kind_of?(Whatever).  If passed an array, it will ensure 
         #            that the value is one of those types.
         # :respond_to:: Ensure that the value has a given method.  Takes one method name or an array of
         #               method names.
         # :required:: Raise an exception if this parameter is missing. Valid values are true or false, 
         #             by default, options are not required.
         # :regex:: Match the value of the paramater against a regular expression.
         # :equal_to:: Match the value of the paramater with ==.  An array means it can be equal to any
         #             of the values.
         def validate(opts, map)
           #--
           # validate works by taking the keys in the validation map, assuming it's a hash, and
           # looking for _pv_:symbol as methods.  Assuming it find them, it calls the right 
           # one.  
           #++
           raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash)
           raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash)   
           
           map.each do |key, validation|
             unless key.kind_of?(Symbol) || key.kind_of?(String)
               raise ArgumentError, "Validation map keys must be symbols or strings!"
             end
             case validation
             when true
               _pv_required(opts, key)
             when false
               true
             when Hash
               validation.each do |check, carg|
                 check_method = "_pv_#{check.to_s}"
                 if self.respond_to?(check_method, true)
                   self.send(check_method, opts, key, carg)
                 else
                   raise ArgumentError, "Validation map has unknown check: #{check}"
                 end
               end
             end
           end
           opts
         end
             
         def set_or_return(symbol, arg, validation)
           iv_symbol = "@#{symbol.to_s}".to_sym
           map = {
             symbol => validation
           }
   
           if arg == nil && self.instance_variable_defined?(iv_symbol) == true
             self.instance_variable_get(iv_symbol)
           else
             opts = validate({ symbol => arg }, { symbol => validation })
             self.instance_variable_set(iv_symbol, opts[symbol])
           end
         end
               
         private
         
           # Return the value of a parameter, or nil if it doesn't exist.
           def _pv_opts_lookup(opts, key)
             if opts.has_key?(key.to_s)
               opts[key.to_s]
             elsif opts.has_key?(key.to_sym)
               opts[key.to_sym]
             else
               nil
             end
           end
           
           # Raise an exception if the parameter is not found.
           def _pv_required(opts, key, is_required=true)
             if is_required
               if (opts.has_key?(key.to_s) && !opts[key.to_s].nil?) ||
                   (opts.has_key?(key.to_sym) && !opts[key.to_sym].nil?)
                 true
               else
                 raise Exceptions::ValidationFailed, "Required argument #{key} is missing!"
               end
             end
           end
           
           def _pv_equal_to(opts, key, to_be)
             value = _pv_opts_lookup(opts, key)
             unless value.nil?
               passes = false
               Array(to_be).each do |tb|
                 passes = true if value == tb
               end
               unless passes
                 raise Exceptions::ValidationFailed, "Option #{key} must be equal to one of: #{to_be.join(", ")}!  You passed #{value.inspect}."
               end
             end
           end
           
           # Raise an exception if the parameter is not a kind_of?(to_be)
           def _pv_kind_of(opts, key, to_be)
             value = _pv_opts_lookup(opts, key)
             unless value.nil?
               passes = false
               Array(to_be).each do |tb|
                 passes = true if value.kind_of?(tb)
               end
               unless passes
                 raise Exceptions::ValidationFailed, "Option #{key} must be a kind of #{to_be}!  You passed #{value.inspect}."
               end
             end
           end
           
           # Raise an exception if the parameter does not respond to a given set of methods.
           def _pv_respond_to(opts, key, method_name_list)
             value = _pv_opts_lookup(opts, key)
             unless value.nil?
               Array(method_name_list).each do |method_name|
                 unless value.respond_to?(method_name)
                   raise Exceptions::ValidationFailed, "Option #{key} must have a #{method_name} method!"
                 end
               end
             end
           end
   
           # Assert that parameter returns false when passed a predicate method.
           # For example, :cannot_be => :blank will raise a Exceptions::ValidationFailed
           # error value.blank? returns a 'truthy' (not nil or false) value.
           #
           # Note, this will *PASS* if the object doesn't respond to the method.
           # So, to make sure a value is not nil and not blank, you need to do
           # both :cannot_be => :blank *and* :cannot_be => :nil (or :required => true)
           def _pv_cannot_be(opts, key, predicate_method_base_name)
             value = _pv_opts_lookup(opts, key)
             predicate_method = (predicate_method_base_name.to_s + "?").to_sym
   
             if value.respond_to?(predicate_method)
               if value.send(predicate_method)
                 raise Exceptions::ValidationFailed, "Option #{key} cannot be #{predicate_method_base_name}"
               end
             end
           end
         
           # Assign a default value to a parameter.
           def _pv_default(opts, key, default_value)
             value = _pv_opts_lookup(opts, key)
             if value == nil
               opts[key] = default_value
             end
           end
           
           # Check a parameter against a regular expression.
           def _pv_regex(opts, key, regex)
             value = _pv_opts_lookup(opts, key)
             if value != nil
               passes = false
               [ regex ].flatten.each do |r|
                 if value != nil
                   if r.match(value.to_s)
                     passes = true
                   end
                 end
               end
               unless passes
                 raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}"
               end
             end
           end
           
           # Check a parameter against a hash of proc's.
           def _pv_callbacks(opts, key, callbacks)
             raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash)
             value = _pv_opts_lookup(opts, key)
             if value != nil
               callbacks.each do |message, zeproc|
                 if zeproc.call(value) != true
                   raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} #{message}!"
                 end
               end
             end
           end
   
           # Allow a parameter to default to @name
           def _pv_name_attribute(opts, key, is_name_attribute=true)
             if is_name_attribute
               if opts[key] == nil
                 opts[key] = self.instance_variable_get("@name")
               end
             end
           end
       end
     end
   end
   

lib/chef/resource/file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class File < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :file
           @path = name
           @backup = 5
           @action = "create"
           @allowed_actions.push(:create, :delete, :touch, :create_if_missing)
         end
   
         def content(arg=nil)
           set_or_return(
             :content,
             arg,
             :kind_of => String
           )
         end
   
         def backup(arg=nil)
           set_or_return(
             :backup,
             arg,
             :kind_of => [ Integer, FalseClass ]
           )
         end
   
         def checksum(arg=nil)
           set_or_return(
             :checksum,
             arg,
             :regex => /^[a-zA-Z0-9]{64}$/
           )
         end
   
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :regex => Chef::Config[:group_valid_regex]
           )
         end
   
         def mode(arg=nil)
           set_or_return(
             :mode,
             arg,
             :callbacks => { 
               "not in valid numeric range" => lambda { |m| 
                 if m.kind_of?(String)
                   m =~ /^0/ || m="0#{m}"
                 end 
                 Integer(m)<=07777 && Integer(m)>=0
               }
             }
           )
         end
   
         def owner(arg=nil)
           set_or_return(
             :owner,
             arg,
             :regex => Chef::Config[:user_valid_regex]
           )
         end
   
         def path(arg=nil)
           set_or_return(
             :path,
             arg,
             :kind_of => String
           )
         end
   
       end
     end
   end

lib/chef/knife/core/generic_presenter.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife/core/text_formatter'
   
   class Chef
     class Knife
       module Core
   
         #==Chef::Knife::Core::GenericPresenter
         # The base presenter class for displaying structured data in knife commands.
         # This is not an abstract base class, and it is suitable for displaying
         # most kinds of objects that knife needs to display.
         class GenericPresenter
   
           attr_reader :ui
           attr_reader :config
   
           # Instaniates a new GenericPresenter. This is generally handled by the
           # Chef::Knife::UI object, though you need to match the signature of this
           # method if you intend to use your own presenter instead.
           def initialize(ui, config)
             @ui, @config = ui, config
           end
   
           # Is the selected output format a data interchange format?
           # Returns true if the selected output format is json or yaml, false
           # otherwise. Knife search uses this to adjust its data output so as not
           # to produce invalid JSON output.
           def interchange?
             case parse_format_option
             when :json, :yaml
               true
             else
               false
             end
           end
   
           # Returns a String representation of +data+ that is suitable for output
           # to a terminal or perhaps for data interchange with another program.
           # The representation of the +data+ depends on the value of the
           # `config[:format]` setting.
           def format(data)
             case parse_format_option
             when :summary
               summarize(data)
             when :text
               text_format(data)
             when :json
               Chef::JSONCompat.to_json_pretty(data)
             when :yaml
               require 'yaml'
               YAML::dump(data)
             when :pp
               require 'stringio'
               # If you were looking for some attribute and there is only one match
               # just dump the attribute value
               if data.length == 1 and config[:attribute]
                 data.values[0]
               else
                 out = StringIO.new
                 PP.pp(data, out)
                 out.string
               end
             end
           end
   
           # Converts the user-supplied value of `config[:format]` to a Symbol
           # representing the desired output format.
           # ===Returns
           # returns one of :summary, :text, :json, :yaml, or :pp
           # ===Raises
           # Raises an ArgumentError if the desired output format could not be
           # determined from the value of `config[:format]`
           def parse_format_option
             case config[:format]
             when "summary", /^s/, nil
               :summary
             when "text", /^t/
               :text
             when "json", /^j/
               :json
             when "yaml", /^y/
               :yaml
             when "pp", /^p/
               :pp
             else
               raise ArgumentError, "Unknown output format #{config[:format]}"
             end
           end
   
           # Summarize the data. Defaults to text format output,
           # which may not be very summary-like
           def summarize(data)
             text_format(data)
           end
   
           # Converts the +data+ to a String in the text format. Uses
           # Chef::Knife::Core::TextFormatter
           def text_format(data)
             TextFormatter.new(data, ui).formatted_data
           end
   
           def format_list_for_display(list)
             config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b }
           end
   
           def format_for_display(data)
             if config[:attribute]
               result = {}
               Array(config[:attribute]).each do |nested_value_spec|
                 nested_value = extract_nested_value(data, nested_value_spec)
                 result[nested_value_spec] = nested_value
               end
               result
             elsif config[:run_list]
               data = data.run_list.run_list
               { "run_list" => data }
             elsif config[:environment]
               if data.respond_to?(:chef_environment)
                 {"chef_environment" => data.chef_environment}
               else
                 # this is a place holder for now. Feel free to modify (i.e. add other cases). [nuo]
                 data
               end
             elsif config[:id_only]
               data.respond_to?(:name) ? data.name : data["id"]
             else
               data
             end
           end
   
           def extract_nested_value(data, nested_value_spec)
             nested_value_spec.split(".").each do |attr|
               if data.nil?
                 nil # don't get no method error on nil
               elsif data.respond_to?(attr.to_sym)
                 data = data.send(attr.to_sym)
               elsif data.respond_to?(:[])
                 data = data[attr]
               else
                 data = begin
                   data.send(attr.to_sym)
                 rescue NoMethodError
                   nil
                 end
               end
             end
             ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
           end
   
           def format_cookbook_list_for_display(item)
             if config[:with_uri]
               item.inject({}) do |collected, (cookbook, versions)|
                 collected[cookbook] = Hash.new
                 versions['versions'].each do |ver|
                   collected[cookbook][ver['version']] = ver['url']
                 end
                 collected
               end
             else
               versions_by_cookbook = item.inject({}) do |collected, ( cookbook, versions )|
                 collected[cookbook] = versions["versions"].map {|v| v['version']}
                 collected
               end
               key_length = versions_by_cookbook.empty? ? 0 : versions_by_cookbook.keys.map {|name| name.size }.max + 2
               versions_by_cookbook.sort.map do |cookbook, versions|
                 "#{cookbook.ljust(key_length)} #{versions.join('  ')}"
               end
             end
           end
   
         end
       end
     end
   end

lib/chef/rest/rest_request.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Thom May ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Author:: Christopher Walters ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'uri'
   require 'net/http'
   require 'chef/rest/cookie_jar'
   
   # To load faster, we only want ohai's version string.
   # However, in ohai before 0.6.0, the version is defined
   # in ohai, not ohai/version
   begin
     require 'ohai/version' #used in user agent string.
   rescue LoadError
     require 'ohai'
   end
   
   require 'chef/version'
   
   class Chef
     class REST
       class RESTRequest
   
         engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
   
         UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +http://opscode.com)"
         DEFAULT_UA = "Chef Client" << UA_COMMON
   
         def self.user_agent=(ua)
           @user_agent = ua
         end
   
         def self.user_agent
           @user_agent ||= DEFAULT_UA
         end
   
         attr_reader :method, :url, :headers, :http_client, :http_request
   
         def initialize(method, url, req_body, base_headers={})
           @method, @url = method, url
           @request_body = nil
           @cookies = CookieJar.instance
           configure_http_client
           build_headers(base_headers)
           configure_http_request(req_body)
         end
   
         def host
           @url.host
         end
   
         def port
           @url.port
         end
   
         def query
           @url.query
         end
   
         def path
           @url.path.empty? ? "/" : @url.path
         end
   
         def call
           hide_net_http_bug do
             http_client.request(http_request) do |response|
               store_cookie(response)
               yield response if block_given?
               response
             end
           end
         end
   
         def config
           Chef::Config
         end
   
         private
   
         def hide_net_http_bug
           yield
         rescue NoMethodError => e
           # http://redmine.ruby-lang.org/issues/show/2708
           # http://redmine.ruby-lang.org/issues/show/2758
           if e.to_s =~ /#{Regexp.escape(%q|undefined method `closed?' for nil:NilClass|)}/
             Chef::Log.debug("Rescued error in http connect, re-raising as Errno::ECONNREFUSED to hide bug in net/http")
             Chef::Log.debug("#{e.class.name}: #{e.to_s}")
             Chef::Log.debug(e.backtrace.join("\n"))
             raise Errno::ECONNREFUSED, "Connection refused attempting to contact #{url.scheme}://#{host}:#{port}"
           else
             raise
           end
         end
   
         def store_cookie(response)
           if response['set-cookie']
             @cookies["#{host}:#{port}"] = response['set-cookie']
           end
         end
   
         def build_headers(headers)
           @headers = headers.dup
           # TODO: need to set accept somewhere else
           # headers.merge!('Accept' => "application/json") unless raw
           @headers['X-Chef-Version'] = ::Chef::VERSION
   
           if @cookies.has_key?("#{host}:#{port}")
             @headers['Cookie'] = @cookies["#{host}:#{port}"]
           end
         end
   
         #adapted from buildr/lib/buildr/core/transports.rb
         def proxy_uri
           proxy = Chef::Config["#{url.scheme}_proxy"]
           proxy = URI.parse(proxy) if String === proxy
           excludes = Chef::Config[:no_proxy].to_s.split(/\s*,\s*/).compact
           excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" }
           return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") }
         end
   
         def configure_http_client
           http_proxy = proxy_uri
           if http_proxy.nil?
             @http_client = Net::HTTP.new(host, port)
           else
             Chef::Log.debug("Using #{http_proxy.host}:#{http_proxy.port} for proxy")
             user = Chef::Config["#{url.scheme}_proxy_user"]
             pass = Chef::Config["#{url.scheme}_proxy_pass"]
             @http_client = Net::HTTP.Proxy(http_proxy.host, http_proxy.port, user, pass).new(host, port)
           end
           if url.scheme == "https"
             @http_client.use_ssl = true
             if config[:ssl_verify_mode] == :verify_none
               @http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE
             elsif config[:ssl_verify_mode] == :verify_peer
               @http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
             end
             if config[:ssl_ca_path]
               unless ::File.exist?(config[:ssl_ca_path])
                 raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_path #{config[:ssl_ca_path]} does not exist"
               end
               @http_client.ca_path = config[:ssl_ca_path]
             elsif config[:ssl_ca_file]
               unless ::File.exist?(config[:ssl_ca_file])
                 raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{config[:ssl_ca_file]} does not exist"
               end
               @http_client.ca_file = config[:ssl_ca_file]
             end
             if (config[:ssl_client_cert] || config[:ssl_client_key])
               unless (config[:ssl_client_cert] && config[:ssl_client_key])
                 raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
               end
               unless ::File.exists?(config[:ssl_client_cert])
                 raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
               end
               unless ::File.exists?(config[:ssl_client_key])
                 raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
               end
               @http_client.cert = OpenSSL::X509::Certificate.new(::File.read(config[:ssl_client_cert]))
               @http_client.key = OpenSSL::PKey::RSA.new(::File.read(config[:ssl_client_key]))
             end
           end
   
           @http_client.read_timeout = config[:rest_timeout]
         end
   
   
         def configure_http_request(request_body=nil)
           req_path = "#{path}"
           req_path << "?#{query}" if query
   
           @http_request = case method.to_s.downcase
           when "get"
             Net::HTTP::Get.new(req_path, headers)
           when "post"
             Net::HTTP::Post.new(req_path, headers)
           when "put"
             Net::HTTP::Put.new(req_path, headers)
           when "delete"
             Net::HTTP::Delete.new(req_path, headers)
           when "head"
             Net::HTTP::Head.new(req_path, headers)
           else
             raise ArgumentError, "You must provide :GET, :PUT, :POST, :DELETE or :HEAD as the method"
           end
   
           @http_request.body = request_body if (request_body && @http_request.request_body_permitted?)
           # Optionally handle HTTP Basic Authentication
           @http_request.basic_auth(url.user, url.password) if url.user
           @http_request['User-Agent'] = self.class.user_agent
         end
   
       end
     end
   end

lib/chef/provider/service/systemd.rb

   #
   # Author:: Stephen Haynes ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/simple'
   require 'chef/mixin/command'
   
   class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
     def load_current_resource
       @current_resource = Chef::Resource::Service.new(@new_resource.name)
       @current_resource.service_name(@new_resource.service_name)
   
       if @new_resource.status_command
         Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
   
         begin
           if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
             @current_resource.running(true)
           end
         rescue Chef::Exceptions::Exec
           @current_resource.running(false)
           nil
         end
       else
         @current_resource.running(is_active?)
       end
   
       @current_resource.enabled(is_enabled?)
       @current_resource
     end
   
     def start_service
       if @current_resource.running
         Chef::Log.debug("#{@new_resource} already running, not starting")
       else
         if @new_resource.start_command
           super
         else
           run_command_with_systems_locale(:command => "/bin/systemctl start #{@new_resource.service_name}")
         end
       end
     end
   
     def stop_service
       unless @current_resource.running
         Chef::Log.debug("#{@new_resource} not running, not stopping")
       else
         if @new_resource.stop_command
           super
         else
           run_command_with_systems_locale(:command => "/bin/systemctl stop #{@new_resource.service_name}")
         end
       end
     end
   
     def restart_service
       if @new_resource.restart_command
         super
       else
         run_command_with_systems_locale(:command => "/bin/systemctl restart #{@new_resource.service_name}")
       end
     end
   
     def reload_service
       if @new_resource.reload_command
         super
       else
         run_command_with_systems_locale(:command => "/bin/systemctl reload #{@new_resource.service_name}")
       end
     end
   
     def enable_service
       run_command_with_systems_locale(:command => "/bin/systemctl enable #{@new_resource.service_name}")
     end 
   
     def disable_service
       run_command_with_systems_locale(:command => "/bin/systemctl disable #{@new_resource.service_name}")
     end 
   
     def is_active?
       run_command_with_systems_locale({:command => "/bin/systemctl is-active #{@new_resource.service_name}", :ignore_failure => true}) == 0
     end
   
     def is_enabled?
       run_command_with_systems_locale({:command => "/bin/systemctl is-enabled #{@new_resource.service_name}", :ignore_failure => true}) == 0
     end
   end

lib/chef/version_constraint.rb

   # Author:: Seth Falcon ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   require 'chef/version_class'
   
   class Chef
     class VersionConstraint
       DEFAULT_CONSTRAINT = ">= 0.0.0"
       STANDARD_OPS = %w(< > <= >=)
       OPS = %w(< > = <= >= ~>)
       PATTERN = /^(#{OPS.join('|')}) (.+)$/
   
       attr_reader :op, :version
   
       def initialize(constraint_spec=DEFAULT_CONSTRAINT)
         case constraint_spec
         when nil
           parse(DEFAULT_CONSTRAINT)
         when Array
           parse_from_array(constraint_spec)
         when String
           parse(constraint_spec)
         else
           msg = "VersionConstraint should be created from a String. You gave: #{constraint_spec.inspect}"
           raise Chef::Exceptions::InvalidVersionConstraint, msg
         end
       end
   
       def include?(v)
         version = if v.respond_to? :version # a CookbookVersion-like object
                     Chef::Version.new(v.version.to_s)
                   else
                     Chef::Version.new(v.to_s)
                   end
        do_op(version)
       end
   
       def inspect
         "(#{@op} #{@version})"
       end
   
       def to_s
         "#{@op} #{@version}"
       end
   
       def eql?(o)
         o.class == self.class && @op == o.op && @version == o.version
       end
       alias_method :==, :eql?
   
       private
   
       def do_op(other_version)
         if STANDARD_OPS.include? @op
           other_version.send(@op.to_sym, @version)
         elsif @op == '='
           other_version == @version
         elsif @op == '~>'
           if @missing_patch_level
             (other_version.major == @version.major &&
              other_version.minor >= @version.minor)
           else
             (other_version.major == @version.major &&
              other_version.minor == @version.minor &&
              other_version.patch >= @version.patch)
           end
         else                      # should never happen
           raise "bad op #{@op}"
         end
       end
   
       def parse_from_array(constraint_spec)
         if constraint_spec.empty?
           parse(DEFAULT_CONSTRAINT)
         elsif constraint_spec.size == 1
           parse(constraint_spec.first)
         else
           msg = "only one version constraint operation is supported, but you gave #{constraint_spec.size} "
           msg << "['#{constraint_spec.join(', ')}']"
           raise Chef::Exceptions::InvalidVersionConstraint, msg
         end
       end
   
       def parse(str)
         @missing_patch_level = false
         if str.index(" ").nil? && str =~ /^[0-9]/
           # try for lone version, implied '='
           @version = Chef::Version.new(str)
           @op = "="
         elsif PATTERN.match str
           @op = $1
           raw_version = $2
           @version = Chef::Version.new(raw_version)
           if raw_version.split('.').size == 2
             @missing_patch_level = true
           end
         else
           raise Chef::Exceptions::InvalidVersionConstraint, "'#{str}'"
         end
       end
   
     end
   end

lib/chef/provider/group.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider'
   require 'chef/mixin/command'
   require 'chef/resource/group'
   require 'etc'
   
   class Chef
     class Provider
       class Group < Chef::Provider
         include Chef::Mixin::Command
         attr_accessor :group_exists
         
         def initialize(new_resource, run_context)
           super
           @group_exists = true
         end
         
         def load_current_resource
           @current_resource = Chef::Resource::Group.new(@new_resource.name)
           @current_resource.group_name(@new_resource.group_name)
           
           group_info = nil
           begin
             group_info = Etc.getgrnam(@new_resource.group_name)
           rescue ArgumentError => e
             @group_exists = false
             Chef::Log.debug("#{@new_resource} group does not exist")
           end
           
           if group_info
             @new_resource.gid(group_info.gid) unless @new_resource.gid
             @current_resource.gid(group_info.gid)
             @current_resource.members(group_info.mem)
           end
           
           @current_resource
         end
         
         # Check to see if a group needs any changes
         #
         # ==== Returns
         # :: If a change is required
         # :: If a change is not required
         def compare_group
           return true if @new_resource.gid != @current_resource.gid
   
           if(@new_resource.append)
             @new_resource.members.each do |member|
               next if @current_resource.members.include?(member)
               return true
             end
           else
             return true if @new_resource.members != @current_resource.members
           end
   
           return false
         end
         
         def action_create
           case @group_exists
           when false
             create_group
             Chef::Log.info("#{@new_resource} created")
             @new_resource.updated_by_last_action(true)
           else 
             if compare_group
               manage_group
               Chef::Log.info("#{@new_resource} altered")
               @new_resource.updated_by_last_action(true)
             end
           end
         end
         
         def action_remove
           if @group_exists
             remove_group
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} removed")
           end
         end
         
         def action_manage
           if @group_exists && compare_group
             manage_group 
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} managed")
           end
         end
         
         def action_modify
           if @group_exists 
             if compare_group
               manage_group
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} modified")
             end
           else
             raise Chef::Exceptions::Group, "Cannot modify #{@new_resource} - group does not exist!"
           end
         end
         
         def create_group
           raise NotImplementedError, "subclasses of Chef::Provider::Group should define #create_group"
         end
   
         def manage_group
           raise NotImplementedError, "subclasses of Chef::Provider::Group should define #manage_group"
         end
   
         def remove_group
           raise NotImplementedError, "subclasses of Chef::Provider::Group should define #remove_group"
         end
   
       end
     end
   end

lib/chef/provider/remote_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/file'
   require 'chef/rest'
   require 'uri'
   require 'tempfile'
   require 'net/https'
   
   class Chef
     class Provider
       class RemoteFile < Chef::Provider::File
   
         def load_current_resource
           super
           @current_resource.checksum(checksum(@current_resource.path)) if ::File.exist?(@current_resource.path)
         end
   
         def action_create
           assert_enclosing_directory_exists!
   
           Chef::Log.debug("#{@new_resource} checking for changes")
   
           if current_resource_matches_target_checksum?
             Chef::Log.debug("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating")
           else
             Chef::REST.new(@new_resource.source, nil, nil).fetch(@new_resource.source) do |raw_file|
               if matches_current_checksum?(raw_file)
                 Chef::Log.debug "#{@new_resource} target and source checksums are the same - not updating"
               else
                 backup_new_resource
                 FileUtils.cp raw_file.path, @new_resource.path
                 Chef::Log.info "#{@new_resource} updated"
                 @new_resource.updated_by_last_action(true)
               end
             end
           end
           enforce_ownership_and_permissions
   
           @new_resource.updated_by_last_action?
         end
   
         def action_create_if_missing
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug("#{@new_resource} exists, taking no action.")
           else
             action_create
           end
         end
   
         def enforce_ownership_and_permissions
           set_owner if @new_resource.owner
           set_group if @new_resource.group
           set_mode  if @new_resource.mode
         end
   
         def current_resource_matches_target_checksum?
           @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{Regexp.escape(@new_resource.checksum)}/
         end
   
         def matches_current_checksum?(candidate_file)
           Chef::Log.debug "#{@new_resource} checking for file existence of #{@new_resource.path}"
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug "#{@new_resource} file exists at #{@new_resource.path}"
             @new_resource.checksum(checksum(candidate_file.path))
             Chef::Log.debug "#{@new_resource} target checksum: #{@current_resource.checksum}"
             Chef::Log.debug "#{@new_resource} source checksum: #{@new_resource.checksum}"
   
             @new_resource.checksum == @current_resource.checksum
           else
             Chef::Log.debug "#{@new_resource} creating #{@new_resource.path}"
             false
           end
         end
   
         def backup_new_resource
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug "#{@new_resource} checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
             backup @new_resource.path
           end
         end
   
         def source_file(source, current_checksum, &block)
           if absolute_uri?(source)
             fetch_from_uri(source, &block)
           elsif !Chef::Config[:solo]
             fetch_from_chef_server(source, current_checksum, &block)
           else
             fetch_from_local_cookbook(source, &block)
           end
         end
   
         private
   
         def absolute_uri?(source)
           URI.parse(source).absolute?
         rescue URI::InvalidURIError
           false
         end
   
       end
     end
   end

lib/chef/monkey_patches/numeric.rb

   unless 0.respond_to?(:fdiv)
     class Numeric
       def fdiv(other)
         to_f / other
       end
     end
   end
   
   # String elements referenced with [] <= 1.8.6 return a Fixnum. Cheat to allow
   # for the simpler "test"[2].ord construct
   class Numeric 
     def ord
       return self
     end
   end

lib/chef/knife.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'forwardable'
   require 'chef/version'
   require 'mixlib/cli'
   require 'chef/mixin/convert_to_class_name'
   require 'chef/knife/core/subcommand_loader'
   require 'chef/knife/core/ui'
   require 'chef/rest'
   require 'pp'
   
   class Chef
     class Knife
   
       Chef::REST::RESTRequest.user_agent = "Chef Knife#{Chef::REST::RESTRequest::UA_COMMON}"
   
       include Mixlib::CLI
       extend Chef::Mixin::ConvertToClassName
       extend Forwardable
   
       # Backwards Compat:
       # Ideally, we should not vomit all of these methods into this base class;
       # instead, they should be accessed by hitting the ui object directly.
       def_delegator :@ui, :stdout
       def_delegator :@ui, :stderr
       def_delegator :@ui, :stdin
       def_delegator :@ui, :msg
       def_delegator :@ui, :ask_question
       def_delegator :@ui, :pretty_print
       def_delegator :@ui, :output
       def_delegator :@ui, :format_list_for_display
       def_delegator :@ui, :format_for_display
       def_delegator :@ui, :format_cookbook_list_for_display
       def_delegator :@ui, :edit_data
       def_delegator :@ui, :edit_object
       def_delegator :@ui, :confirm
   
       attr_accessor :name_args
       attr_accessor :ui
   
       def self.ui
         @ui ||= Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
       end
   
       def self.msg(msg="")
         ui.msg(msg)
       end
   
       def self.reset_subcommands!
         @@subcommands = {}
         @subcommands_by_category = nil
       end
   
       def self.inherited(subclass)
         unless subclass.unnamed?
           subcommands[subclass.snake_case_name] = subclass
         end
       end
   
       # Explicitly set the category for the current command to +new_category+
       # The category is normally determined from the first word of the command
       # name, but some commands make more sense using two or more words
       # ===Arguments
       # new_category::: A String to set the category to (see examples)
       # ===Examples:
       # Data bag commands would be in the 'data' category by default. To put them
       # in the 'data bag' category:
       #   category('data bag')
       def self.category(new_category)
         @category = new_category
       end
   
       def self.subcommand_category
         @category || snake_case_name.split('_').first unless unnamed?
       end
   
       def self.snake_case_name
         convert_to_snake_case(name.split('::').last) unless unnamed?
       end
   
       def self.common_name
         snake_case_name.split('_').join(' ')
       end
   
       # Does this class have a name? (Classes created via Class.new don't)
       def self.unnamed?
         name.nil? || name.empty?
       end
   
       def self.subcommand_loader
         @subcommand_loader ||= Knife::SubcommandLoader.new(chef_config_dir)
       end
   
       def self.load_commands
         @commands_loaded ||= subcommand_loader.load_commands
       end
   
       def self.subcommands
         @@subcommands ||= {}
       end
   
       def self.subcommands_by_category
         unless @subcommands_by_category
           @subcommands_by_category = Hash.new { |hash, key| hash[key] = [] }
           subcommands.each do |snake_cased, klass|
             @subcommands_by_category[klass.subcommand_category] << snake_cased
           end
         end
         @subcommands_by_category
       end
   
       # Print the list of subcommands knife knows about. If +preferred_category+
       # is given, only subcommands in that category are shown
       def self.list_commands(preferred_category=nil)
         load_commands
   
         category_desc = preferred_category ? preferred_category + " " : ''
         msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
   
         if preferred_category && subcommands_by_category.key?(preferred_category)
           commands_to_show = {preferred_category => subcommands_by_category[preferred_category]}
         else
           commands_to_show = subcommands_by_category
         end
   
         commands_to_show.sort.each do |category, commands|
           next if category =~ /deprecated/i
           msg "** #{category.upcase} COMMANDS **"
           commands.each do |command|
             msg subcommands[command].banner if subcommands[command]
           end
           msg
         end
       end
   
       # Run knife for the given +args+ (ARGV), adding +options+ to the list of
       # CLI options that the subcommand knows how to handle.
       # ===Arguments
       # args::: usually ARGV
       # options::: A Mixlib::CLI option parser hash. These +options+ are how
       # subcommands know about global knife CLI options
       def self.run(args, options={})
         load_commands
         subcommand_class = subcommand_class_from(args)
         subcommand_class.options = options.merge!(subcommand_class.options)
         subcommand_class.load_deps unless want_help?(args)
         instance = subcommand_class.new(args)
         instance.configure_chef
         instance.run_with_pretty_exceptions
       end
   
       def self.guess_category(args)
         category_words = args.select {|arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
         category_words.map! {|w| w.split('-')}.flatten!
         matching_category = nil
         while (!matching_category) && (!category_words.empty?)
           candidate_category = category_words.join(' ')
           matching_category = candidate_category if subcommands_by_category.key?(candidate_category)
           matching_category || category_words.pop
         end
         matching_category
       end
   
       def self.subcommand_class_from(args)
         command_words = args.select {|arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
   
         subcommand_class = nil
   
         while ( !subcommand_class ) && ( !command_words.empty? )
           snake_case_class_name = command_words.join("_")
           unless subcommand_class = subcommands[snake_case_class_name]
             command_words.pop
           end
         end
         # see if we got the command as e.g., knife node-list
         subcommand_class ||= subcommands[args.first.gsub('-', '_')]
         subcommand_class || subcommand_not_found!(args)
       end
   
       def self.deps(&block)
         @dependency_loader = block
       end
   
       def self.load_deps
         @dependency_loader && @dependency_loader.call
       end
   
       private
   
       OFFICIAL_PLUGINS = %w[ec2 rackspace windows openstack terremark bluebox]
   
       # :nodoc:
       # Error out and print usage. probably becuase the arguments given by the
       # user could not be resolved to a subcommand.
       def self.subcommand_not_found!(args)
         unless want_help?(args)
           ui.fatal("Cannot find sub command for: '#{args.join(' ')}'")
         end
   
         if category_commands = guess_category(args)
           list_commands(category_commands)
         elsif missing_plugin = ( OFFICIAL_PLUGINS.find {|plugin| plugin == args[0]} )
           ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
           ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}")
         else
           list_commands
         end
   
         exit 10
       end
   
       # :nodoc:
       # TODO: duplicated with chef/application/knife
       # all logic should be removed from that and Chef::Knife should own it.
       def self.want_help?(args)
         (args.any? { |arg| arg =~ /^(:?(:?\-\-)?help|\-h)$/})
       end
   
       @@chef_config_dir = nil
   
       # search upward from current_dir until .chef directory is found
       def self.chef_config_dir
         if @@chef_config_dir.nil? # share this with subclasses
           @@chef_config_dir = false
           full_path = Dir.pwd.split(File::SEPARATOR)
           (full_path.length - 1).downto(0) do |i|
             canidate_directory = File.join(full_path[0..i] + [".chef" ])
             if File.exist?(canidate_directory) && File.directory?(canidate_directory)
               @@chef_config_dir = canidate_directory
               break
             end
           end
         end
         @@chef_config_dir
       end
   
   
       public
   
       # Create a new instance of the current class configured for the given
       # arguments and options
       def initialize(argv=[])
         super() # having to call super in initialize is the most annoying anti-pattern :(
         @ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, config)
   
         command_name_words = self.class.snake_case_name.split('_')
   
         # Mixlib::CLI ignores the embedded name_args
         @name_args = parse_options(argv)
         @name_args.delete(command_name_words.join('-'))
         @name_args.reject! { |name_arg| command_name_words.delete(name_arg) }
   
         # knife node run_list add requires that we have extra logic to handle
         # the case that command name words could be joined by an underscore :/
         command_name_words = command_name_words.join('_')
         @name_args.reject! { |name_arg| command_name_words == name_arg }
   
         if config[:help]
           msg opt_parser
           exit 1
         end
       end
   
       def parse_options(args)
         super
       rescue OptionParser::InvalidOption => e
         puts "Error: " + e.to_s
         show_usage
         exit(1)
       end
   
       def configure_chef
         unless config[:config_file]
           if self.class.chef_config_dir
             candidate_config = File.expand_path('knife.rb',self.class.chef_config_dir)
             config[:config_file] = candidate_config if File.exist?(candidate_config)
           end
           # If we haven't set a config yet and $HOME is set, and the home
           # knife.rb exists, use it:
           if (!config[:config_file]) && ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'knife.rb'))
             config[:config_file] = File.join(ENV['HOME'], '.chef', 'knife.rb')
           end
         end
   
         # Don't try to load a knife.rb if it doesn't exist.
         if config[:config_file]
           read_config_file(config[:config_file])
         else
           # ...but do log a message if no config was found.
           Chef::Config[:color] = config[:color] && !config[:no_color]
           ui.warn("No knife configuration file found")
         end
   
         Chef::Config[:color] = config[:color] && !config[:no_color]
   
         case config[:verbosity]
         when 0
           Chef::Config[:log_level] = :error
         when 1
           Chef::Config[:log_level] = :info
         else
           Chef::Config[:log_level] = :debug
         end
   
         Chef::Config[:node_name]         = config[:node_name]       if config[:node_name]
         Chef::Config[:client_key]        = config[:client_key]      if config[:client_key]
         Chef::Config[:chef_server_url]   = config[:chef_server_url] if config[:chef_server_url]
         Chef::Config[:environment]       = config[:environment]     if config[:environment]
   
         # Expand a relative path from the config directory. Config from command
         # line should already be expanded, and absolute paths will be unchanged.
         if Chef::Config[:client_key] && config[:config_file]
           Chef::Config[:client_key] = File.expand_path(Chef::Config[:client_key], File.dirname(config[:config_file]))
         end
   
         Mixlib::Log::Formatter.show_time = false
         Chef::Log.init(Chef::Config[:log_location])
         Chef::Log.level(Chef::Config[:log_level] || :error)
   
         Chef::Log.debug("Using configuration from #{config[:config_file]}")
   
         if Chef::Config[:node_name].nil?
           #raise ArgumentError, "No user specified, pass via -u or specifiy 'node_name' in #{config[:config_file] ? config[:config_file] : "~/.chef/knife.rb"}"
         end
       end
   
       def read_config_file(file)
         Chef::Config.from_file(file)
       rescue SyntaxError => e
         ui.error "You have invalid ruby syntax in your config file #{file}"
         ui.info(ui.color(e.message, :red))
         if file_line = e.message[/#{Regexp.escape(file)}:[\d]+/]
           line = file_line[/:([\d]+)$/, 1].to_i
           highlight_config_error(file, line)
         end
         exit 1
       rescue Exception => e
         ui.error "You have an error in your config file #{file}"
         ui.info "#{e.class.name}: #{e.message}"
         filtered_trace = e.backtrace.grep(/#{Regexp.escape(file)}/)
         filtered_trace.each {|line| ui.msg("  " + ui.color(line, :red))}
         if !filtered_trace.empty?
           line_nr = filtered_trace.first[/#{Regexp.escape(file)}:([\d]+)/, 1]
           highlight_config_error(file, line_nr.to_i)
         end
   
         exit 1
       end
   
       def highlight_config_error(file, line)
         config_file_lines = []
         IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"}
         if line == 1
           lines = config_file_lines[0..3]
           lines[0] = ui.color(lines[0], :red)
         else
           lines = config_file_lines[Range.new(line - 2, line)]
           lines[1] = ui.color(lines[1], :red)
         end
         ui.msg ""
         ui.msg ui.color("     # #{file}", :white)
         lines.each {|l| ui.msg(l)}
         ui.msg ""
       end
   
       def show_usage
         stdout.puts("USAGE: " + self.opt_parser.to_s)
       end
   
       def run_with_pretty_exceptions
         unless self.respond_to?(:run)
           ui.error "You need to add a #run method to your knife command before you can use it"
         end
         run
       rescue Exception => e
         raise if config[:verbosity] == 2
         humanize_exception(e)
         exit 100
       end
   
       def humanize_exception(e)
         case e
         when SystemExit
           raise # make sure exit passes through.
         when Net::HTTPServerException, Net::HTTPFatalError
           humanize_http_exception(e)
         when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError
           ui.error "Network Error: #{e.message}"
           ui.info "Check your knife configuration and network settings"
         when NameError, NoMethodError
           ui.error "knife encountered an unexpected error"
           ui.info  "This may be a bug in the '#{self.class.common_name}' knife command or plugin"
           ui.info  "Please collect the output of this command with the `-VV` option before filing a bug report."
           ui.info  "Exception: #{e.class.name}: #{e.message}"
         when Chef::Exceptions::PrivateKeyMissing
           ui.error "Your private key could not be loaded from #{api_key}"
           ui.info  "Check your configuration file and ensure that your private key is readable"
         else
           ui.error "#{e.class.name}: #{e.message}"
         end
       end
   
       def humanize_http_exception(e)
         response = e.response
         case response
         when Net::HTTPUnauthorized
           ui.error "Failed to authenticate to #{server_url} as #{username} with key #{api_key}"
           ui.info "Response:  #{format_rest_error(response)}"
         when Net::HTTPForbidden
           ui.error "You authenticated successfully to #{server_url} as #{username} but you are not authorized for this action"
           ui.info "Response:  #{format_rest_error(response)}"
         when Net::HTTPBadRequest
           ui.error "The data in your request was invalid"
           ui.info "Response: #{format_rest_error(response)}"
         when Net::HTTPNotFound
           ui.error "The object you are looking for could not be found"
           ui.info "Response: #{format_rest_error(response)}"
         when Net::HTTPInternalServerError
           ui.error "internal server error"
           ui.info "Response: #{format_rest_error(response)}"
         when Net::HTTPBadGateway
           ui.error "bad gateway"
           ui.info "Response: #{format_rest_error(response)}"
         when Net::HTTPServiceUnavailable
           ui.error "Service temporarily unavailable"
           ui.info "Response: #{format_rest_error(response)}"
         else
           ui.error response.message
           ui.info "Response: #{format_rest_error(response)}"
         end
       end
   
       def username
         Chef::Config[:node_name]
       end
   
       def api_key
         Chef::Config[:client_key]
       end
   
       # Parses JSON from the error response sent by Chef Server and returns the
       # error message
       #--
       # TODO: this code belongs in Chef::REST
       def format_rest_error(response)
         Array(Chef::JSONCompat.from_json(response.body)["error"]).join('; ')
       rescue Exception
         response.body
       end
   
       def create_object(object, pretty_name=nil, &block)
         output = edit_data(object)
   
         if Kernel.block_given?
           output = block.call(output)
         else
           output.save
         end
   
         pretty_name ||= output
   
         self.msg("Created #{pretty_name}")
   
         output(output) if config[:print_after]
       end
   
       def delete_object(klass, name, delete_name=nil, &block)
         confirm("Do you really want to delete #{name}")
   
         if Kernel.block_given?
           object = block.call
         else
           object = klass.load(name)
           object.destroy
         end
   
         output(format_for_display(object)) if config[:print_after]
   
         obj_name = delete_name ? "#{delete_name}[#{name}]" : object
         self.msg("Deleted #{obj_name}")
       end
   
       def rest
         @rest ||= begin
           require 'chef/rest'
           Chef::REST.new(Chef::Config[:chef_server_url])
         end
       end
   
       def noauth_rest
         @rest ||= begin
           require 'chef/rest'
           Chef::REST.new(Chef::Config[:chef_server_url], false, false)
         end
       end
   
       def server_url
         Chef::Config[:chef_server_url]
       end
   
     end
   end
   

lib/chef/node.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Author:: Christopher Walters ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2008-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'forwardable'
   require 'chef/config'
   require 'chef/cookbook/cookbook_collection'
   require 'chef/nil_argument'
   require 'chef/mixin/check_helper'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/mixin/language_include_attribute'
   require 'chef/mixin/deep_merge'
   require 'chef/environment'
   require 'chef/couchdb'
   require 'chef/rest'
   require 'chef/run_list'
   require 'chef/node/attribute'
   require 'chef/index_queue'
   require 'chef/mash'
   require 'chef/json_compat'
   require 'chef/search/query'
   
   class Chef
     class Node
   
       extend Forwardable
   
       def_delegators :construct_attributes, :keys, :each_key, :each_value, :key?, :has_key?
   
       attr_accessor :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list
       attr_accessor :override_attrs, :default_attrs, :normal_attrs, :automatic_attrs
       attr_reader :couchdb_id
   
       # TODO: 5/18/2010 cw/timh. cookbook_collection should be removed
       # from here and for any place it's needed, it should be accessed
       # through a Chef::RunContext
       attr_accessor :cookbook_collection
   
       include Chef::Mixin::CheckHelper
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       include Chef::Mixin::LanguageIncludeAttribute
       include Chef::IndexQueue::Indexable
   
       DESIGN_DOCUMENT = {
         "version" => 11,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "node") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "node") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           },
           "status" => {
             "map" => <<-EOJS
               function(doc) {
                 if (doc.chef_type == "node") {
                   var to_emit = { "name": doc.name, "chef_environment": doc.chef_environment };
                   if (doc["attributes"]["fqdn"]) {
                     to_emit["fqdn"] = doc["attributes"]["fqdn"];
                   } else {
                     to_emit["fqdn"] = "Undefined";
                   }
                   if (doc["attributes"]["ipaddress"]) {
                     to_emit["ipaddress"] = doc["attributes"]["ipaddress"];
                   } else {
                     to_emit["ipaddress"] = "Undefined";
                   }
                   if (doc["attributes"]["ohai_time"]) {
                     to_emit["ohai_time"] = doc["attributes"]["ohai_time"];
                   } else {
                     to_emit["ohai_time"] = "Undefined";
                   }
                   if (doc["attributes"]["uptime"]) {
                     to_emit["uptime"] = doc["attributes"]["uptime"];
                   } else {
                     to_emit["uptime"] = "Undefined";
                   }
                   if (doc["attributes"]["platform"]) {
                     to_emit["platform"] = doc["attributes"]["platform"];
                   } else {
                     to_emit["platform"] = "Undefined";
                   }
                   if (doc["attributes"]["platform_version"]) {
                     to_emit["platform_version"] = doc["attributes"]["platform_version"];
                   } else {
                     to_emit["platform_version"] = "Undefined";
                   }
                   if (doc["run_list"]) {
                     to_emit["run_list"] = doc["run_list"];
                   } else {
                     to_emit["run_list"] = "Undefined";
                   }
                   emit(doc.name, to_emit);
                 }
               }
             EOJS
           },
           "by_run_list" => {
             "map" => <<-EOJS
               function(doc) {
                 if (doc.chef_type == "node") {
                   if (doc['run_list']) {
                     for (var i=0; i < doc.run_list.length; i++) {
                       emit(doc['run_list'][i], doc.name);
                     }
                   }
                 }
               }
             EOJS
           },
           "by_environment" => {
             "map" => <<-EOJS
               function(doc) {
                 if (doc.chef_type == "node") {
                   var env = (doc['chef_environment'] == null ? "_default" : doc['chef_environment']);
                   emit(env, doc.name);
                 }
               }
             EOJS
           }
         },
       }
   
       # Create a new Chef::Node object.
       def initialize(couchdb=nil)
         @name = nil
   
         @chef_environment = '_default'
         @normal_attrs = Mash.new
         @override_attrs = Mash.new
         @default_attrs = Mash.new
         @automatic_attrs = Mash.new
         @run_list = Chef::RunList.new
   
         @couchdb_rev = nil
         @couchdb_id = nil
         @couchdb = couchdb || Chef::CouchDB.new
   
         @run_state = {
           :template_cache => Hash.new,
           :seen_recipes => Hash.new,
           :seen_attributes => Hash.new
         }
         # TODO: 5/20/2010 need this here as long as other objects try to access
         # the cookbook collection via Node, otherwise get NoMethodError on nil.
         @cookbook_collection = CookbookCollection.new
       end
   
       def couchdb_id=(value)
         @couchdb_id = value
         @index_id = value
       end
   
       # Used by DSL
       def node
         self
       end
   
       def chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       # Find a recipe for this Chef::Node by fqdn.  Will search first for
       # Chef::Config["node_path"]/fqdn.rb, then hostname.rb, then default.rb.
       #
       # Returns a new Chef::Node object.
       #
       # Raises an ArgumentError if it cannot find the node.
       def find_file(fqdn)
         host_parts = fqdn.split(".")
         hostname = host_parts[0]
   
         [fqdn, hostname, "default"].each { |fname|
          node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
          return self.from_file(node_file) if File.exists?(node_file)
        }
   
         raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
       end
   
       # Set the name of this Node, or return the current name.
       def name(arg=nil)
         if arg != nil
           validate(
                    {:name => arg },
                    {:name => { :kind_of => String,
                        :cannot_be => :blank,
                        :regex => /^[\-[:alnum:]_:.]+$/}
                    })
           @name = arg
         else
           @name
         end
       end
   
       def chef_environment(arg=nil)
         set_or_return(
           :chef_environment,
           arg,
           { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String }
         )
       end
   
       # Used by the DSL
       def attribute
         construct_attributes
       end
   
       def construct_attributes
         Chef::Node::Attribute.new(normal_attrs, default_attrs, override_attrs, automatic_attrs)
       end
   
       def attribute=(value)
         self.normal_attrs = value
       end
   
       # Return an attribute of this node.  Returns nil if the attribute is not found.
       def [](attrib)
         construct_attributes[attrib]
       end
   
       # Set an attribute of this node
       def []=(attrib, value)
         construct_attributes[attrib] = value
       end
   
       def store(attrib, value)
         self[attrib] = value
       end
   
       # Set a normal attribute of this node, but auto-vivifiy any Mashes that
       # might be missing
       def normal
         attrs = construct_attributes
         attrs.set_type = :normal
         attrs.auto_vivifiy_on_read = true
         attrs
       end
   
       alias_method :set, :normal
   
       # Set a normal attribute of this node, auto-vivifiying any mashes that are
       # missing, but if the final value already exists, don't set it
       def normal_unless
         attrs = construct_attributes
         attrs.set_type = :normal
         attrs.auto_vivifiy_on_read = true
         attrs.set_unless_value_present = true
         attrs
       end
       alias_method :set_unless, :normal_unless
   
       # Set a default of this node, but auto-vivifiy any Mashes that might
       # be missing
       def default
         attrs = construct_attributes
         attrs.set_type = :default
         attrs.auto_vivifiy_on_read = true
         attrs
       end
   
       # Set a default attribute of this node, auto-vivifiying any mashes that are
       # missing, but if the final value already exists, don't set it
       def default_unless
         attrs = construct_attributes
         attrs.set_type = :default
         attrs.auto_vivifiy_on_read = true
         attrs.set_unless_value_present = true
         attrs
       end
   
       # Set an override attribute of this node, but auto-vivifiy any Mashes that
       # might be missing
       def override
         attrs = construct_attributes
         attrs.set_type = :override
         attrs.auto_vivifiy_on_read = true
         attrs
       end
   
       # Set an override attribute of this node, auto-vivifiying any mashes that
       # are missing, but if the final value already exists, don't set it
       def override_unless
         attrs = construct_attributes
         attrs.set_type = :override
         attrs.auto_vivifiy_on_read = true
         attrs.set_unless_value_present = true
         attrs
       end
   
       # Return true if this Node has a given attribute, false if not.  Takes either a symbol or
       # a string.
       #
       # Only works on the top level. Preferred way is to use the normal [] style
       # lookup and call attribute?()
       def attribute?(attrib)
         construct_attributes.attribute?(attrib)
       end
   
       # Yield each key of the top level to the block.
       def each(&block)
         construct_attributes.each(&block)
       end
   
       # Iterates over each attribute, passing the attribute and value to the block.
       def each_attribute(&block)
         construct_attributes.each_attribute(&block)
       end
   
       # Encouraged to only get used for lookups - while you can do sets from here, it's not as explicit
       # as using the normal/default/override interface.
       def method_missing(symbol, *args)
         attrs = construct_attributes
         attrs.send(symbol, *args)
       end
   
       # Returns true if this Node expects a given recipe, false if not.
       #
       # First, the run list is consulted to see whether the recipe is
       # explicitly included. If it's not there, it looks in
       # run_state[:seen_recipes], which is populated by include_recipe
       # statements in the DSL (and thus would not be in the run list).
       #
       # NOTE: It's used by cookbook authors
       def recipe?(recipe_name)
         run_list.include?(recipe_name) || run_state[:seen_recipes].include?(recipe_name)
       end
   
       # Returns true if this Node expects a given role, false if not.
       def role?(role_name)
         run_list.include?("role[#{role_name}]")
       end
   
       # Returns an Array of roles and recipes, in the order they will be applied.
       # If you call it with arguments, they will become the new list of roles and recipes.
       def run_list(*args)
         args.length > 0 ? @run_list.reset!(args) : @run_list
       end
   
       # Returns true if this Node expects a given role, false if not.
       def run_list?(item)
         run_list.detect { |r| r == item } ? true : false
       end
   
       # Consume data from ohai and Attributes provided as JSON on the command line.
       def consume_external_attrs(ohai_data, json_cli_attrs)
         Chef::Log.debug("Extracting run list from JSON attributes provided on command line")
         consume_attributes(json_cli_attrs)
   
         @automatic_attrs = ohai_data
   
         platform, version = Chef::Platform.find_platform_and_version(self)
         Chef::Log.debug("Platform is #{platform} version #{version}")
         @automatic_attrs[:platform] = platform
         @automatic_attrs[:platform_version] = version
       end
   
       # Consumes the combined run_list and other attributes in +attrs+
       def consume_attributes(attrs)
         normal_attrs_to_merge = consume_run_list(attrs)
         Chef::Log.debug("Applying attributes from json file")
         @normal_attrs = Chef::Mixin::DeepMerge.merge(@normal_attrs,normal_attrs_to_merge)
         self.tags # make sure they're defined
       end
   
       # Lazy initializer for tags attribute
       def tags
         self[:tags] = [] unless attribute?(:tags)
         self[:tags]
       end
   
       # Extracts the run list from +attrs+ and applies it. Returns the remaining attributes
       def consume_run_list(attrs)
         attrs = attrs ? attrs.dup : {}
         if new_run_list = attrs.delete("recipes") || attrs.delete("run_list")
           if attrs.key?("recipes") || attrs.key?("run_list")
             raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
           end
           Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from JSON")
           run_list(new_run_list)
         end
         attrs
       end
   
       # Clear defaults and overrides, so that any deleted attributes
       # between runs are still gone.
       def reset_defaults_and_overrides
         @default_attrs = Mash.new
         @override_attrs = Mash.new
       end
   
       # Expands the node's run list and sets the default and override
       # attributes. Also applies stored attributes (from json provided
       # on the command line)
       #
       # Returns the fully-expanded list of recipes, a RunListExpansion.
       #
       #--
       # TODO: timh/cw, 5-14-2010: Should this method exist? Should we
       # instead modify default_attrs and override_attrs whenever our
       # run_list is mutated? Or perhaps do something smarter like
       # on-demand generation of default_attrs and override_attrs,
       # invalidated only when run_list is mutated?
       def expand!(data_source = 'server')
         expansion = run_list.expand(chef_environment, data_source)
         raise Chef::Exceptions::MissingRole if expansion.errors?
   
         self.tags # make sure they're defined
   
         @automatic_attrs[:recipes] = expansion.recipes
         @automatic_attrs[:roles] = expansion.roles
   
         expansion
       end
   
       # Apply the default and overrides attributes from the expansion
       # passed in, which came from roles.
       def apply_expansion_attributes(expansion)
         load_chef_environment_object = (chef_environment == "_default" ? nil : Chef::Environment.load(chef_environment))
         environment_default_attrs = load_chef_environment_object.nil? ? {} : load_chef_environment_object.default_attributes
         default_before_roles = Chef::Mixin::DeepMerge.merge(default_attrs, environment_default_attrs)
         @default_attrs = Chef::Mixin::DeepMerge.merge(default_before_roles, expansion.default_attrs)
         environment_override_attrs = load_chef_environment_object.nil? ? {} : load_chef_environment_object.override_attributes
         overrides_before_environments = Chef::Mixin::DeepMerge.merge(override_attrs, expansion.override_attrs)
         @override_attrs = Chef::Mixin::DeepMerge.merge(overrides_before_environments, environment_override_attrs)
       end
   
       # Transform the node to a Hash
       def to_hash
         index_hash = Hash.new
         index_hash["chef_type"] = "node"
         index_hash["name"] = name
         index_hash["chef_environment"] = chef_environment
         attribute.each do |key, value|
           index_hash[key] = value
         end
         index_hash["recipe"] = run_list.recipe_names if run_list.recipe_names.length > 0
         index_hash["role"] = run_list.role_names if run_list.role_names.length > 0
         index_hash["run_list"] = run_list.run_list if run_list.run_list.length > 0
         index_hash
       end
   
       def display_hash
         display = {}
         display["name"]             = name
         display["chef_environment"] = chef_environment
         display["automatic"]        = automatic_attrs
         display["normal"]           = normal_attrs
         display["default"]          = default_attrs
         display["override"]         = override_attrs
         display["run_list"]         = run_list.run_list
         display
       end
   
       # Serialize this object as a hash
       def to_json(*a)
         result = {
           "name" => name,
           "chef_environment" => chef_environment,
           'json_class' => self.class.name,
           "automatic" => automatic_attrs,
           "normal" => normal_attrs,
           "chef_type" => "node",
           "default" => default_attrs,
           "override" => override_attrs,
           "run_list" => run_list.run_list
         }
         result["_rev"] = couchdb_rev if couchdb_rev
         result.to_json(*a)
       end
   
       def update_from!(o)
         run_list.reset!(o.run_list)
         @automatic_attrs = o.automatic_attrs
         @normal_attrs = o.normal_attrs
         @override_attrs = o.override_attrs
         @default_attrs = o.default_attrs
         chef_environment(o.chef_environment)
         self
       end
   
       # Create a Chef::Node from JSON
       def self.json_create(o)
         node = new
         node.name(o["name"])
         node.chef_environment(o["chef_environment"])
         if o.has_key?("attributes")
           node.normal_attrs = o["attributes"]
         end
         node.automatic_attrs = Mash.new(o["automatic"]) if o.has_key?("automatic")
         node.normal_attrs = Mash.new(o["normal"]) if o.has_key?("normal")
         node.default_attrs = Mash.new(o["default"]) if o.has_key?("default")
         node.override_attrs = Mash.new(o["override"]) if o.has_key?("override")
   
         if o.has_key?("run_list")
           node.run_list.reset!(o["run_list"])
         else
           o["recipes"].each { |r| node.recipes << r }
         end
         node.couchdb_rev = o["_rev"] if o.has_key?("_rev")
         node.couchdb_id = o["_id"] if o.has_key?("_id")
         node.index_id = node.couchdb_id
         node
       end
   
       def self.cdb_list_by_environment(environment, inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).get_view("nodes", "by_environment", :include_docs => inflate, :startkey => environment, :endkey => environment)
         inflate ? rs["rows"].collect {|r| r["doc"]} : rs["rows"].collect {|r| r["value"]}
       end
   
       def self.list_by_environment(environment, inflate=false)
         if inflate
           response = Hash.new
           Chef::Search::Query.new.search(:node, "chef_environment:#{environment}") {|n| response[n.name] = n unless n.nil?}
           response
         else
           Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("environments/#{environment}/nodes")
         end
       end
   
       # List all the Chef::Node objects in the CouchDB.  If inflate is set to true, you will get
       # the full list of all Nodes, fully inflated.
       def self.cdb_list(inflate=false, couchdb=nil)
         rs =(couchdb || Chef::CouchDB.new).list("nodes", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }
       end
   
       def self.list(inflate=false)
         if inflate
           response = Hash.new
           Chef::Search::Query.new.search(:node) do |n|
             response[n.name] = n unless n.nil?
           end
           response
         else
           Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes")
         end
       end
   
       # Load a node by name from CouchDB
       def self.cdb_load(name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("node", name)
       end
   
       def self.exists?(nodename, couchdb)
         begin
           self.cdb_load(nodename, couchdb)
         rescue Chef::Exceptions::CouchDBNotFound
           nil
         end
       end
   
       def self.find_or_create(node_name)
         load(node_name)
       rescue Net::HTTPServerException => e
         raise unless e.response.code == '404'
         node = build(node_name)
         node.create
       end
   
       def self.build(node_name)
         node = new
         node.name(node_name)
         node.chef_environment(Chef::Config[:environment]) unless Chef::Config[:environment].nil? || Chef::Config[:environment].chop.empty?
         node
       end
   
       # Load a node by name
       def self.load(name)
         Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}")
       end
   
       # Remove this node from the CouchDB
       def cdb_destroy
         couchdb.delete("node", name, couchdb_rev)
       end
   
       # Remove this node via the REST API
       def destroy
         chef_server_rest.delete_rest("nodes/#{name}")
       end
   
       # Save this node to the CouchDB
       def cdb_save
         @couchdb_rev = couchdb.store("node", name, self)["rev"]
       end
   
       # Save this node via the REST API
       def save
         # Try PUT. If the node doesn't yet exist, PUT will return 404,
         # so then POST to create.
         begin
           chef_server_rest.put_rest("nodes/#{name}", self)
         rescue Net::HTTPServerException => e
           raise e unless e.response.code == "404"
           chef_server_rest.post_rest("nodes", self)
         end
         self
       end
   
       # Create the node via the REST API
       def create
         chef_server_rest.post_rest("nodes", self)
         self
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("nodes", DESIGN_DOCUMENT)
       end
   
       def to_s
         "node[#{name}]"
       end
   
       # Load all attribute files for all cookbooks associated with this
       # node.
       def load_attributes
         cookbook_collection.values.each do |cookbook|
           cookbook.segment_filenames(:attributes).each do |segment_filename|
             Chef::Log.debug("Node #{name} loading cookbook #{cookbook.name}'s attribute file #{segment_filename}")
             self.from_file(segment_filename)
           end
         end
       end
   
       # Used by DSL.
       # Loads the attribute file specified by the short name of the
       # file, e.g., loads specified cookbook's
       #   "attributes/mailservers.rb"
       # if passed
       #   "mailservers"
       def load_attribute_by_short_filename(name, src_cookbook_name)
         src_cookbook = cookbook_collection[src_cookbook_name]
         raise Chef::Exceptions::CookbookNotFound, "could not find cookbook #{src_cookbook_name} while loading attribute #{name}" unless src_cookbook
   
         attribute_filename = src_cookbook.attribute_filenames_by_short_filename[name]
         raise Chef::Exceptions::AttributeNotFound, "could not find filename for attribute #{name} in cookbook #{src_cookbook_name}" unless attribute_filename
   
         self.from_file(attribute_filename)
         self
       end
     end
   end

lib/chef/provider/remote_directory.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/file'
   require 'chef/provider/directory'
   require 'chef/resource/directory'
   require 'chef/resource/remote_file'
   require 'chef/platform'
   require 'uri'
   require 'tempfile'
   require 'net/https'
   require 'set'
   
   class Chef
     class Provider
       class RemoteDirectory < Chef::Provider::Directory
   
         def action_create
           super
   
           files_to_purge = Set.new(
             Dir.glob(::File.join(@new_resource.path, '**', '*'), ::File::FNM_DOTMATCH).select do |name|
               name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/
             end
           )
           files_to_transfer.each do |cookbook_file_relative_path|
             create_cookbook_file(cookbook_file_relative_path)
             files_to_purge.delete(::File.dirname(::File.join(@new_resource.path, cookbook_file_relative_path)))
             files_to_purge.delete(::File.join(@new_resource.path, cookbook_file_relative_path))
           end
           purge_unmanaged_files(files_to_purge)
         end
   
         def action_create_if_missing
           # if this action is called, ignore the existing overwrite flag
           @new_resource.overwrite = false
           action_create
         end
   
         protected
   
         def purge_unmanaged_files(unmanaged_files)
           if @new_resource.purge
             unmanaged_files.sort.reverse.each do |f|
               if ::File.directory?(f)
                 Dir::rmdir(f)
                 Chef::Log.debug("#{@new_resource} removed directory #{f}")
               else
                 ::File.delete(f)
                 Chef::Log.debug("#{@new_resource} deleted file #{f}")
               end
             end
           end
         end
   
         def files_to_transfer
           cookbook = run_context.cookbook_collection[resource_cookbook]
           files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source)
           files.sort.reverse
         end
   
         def directory_root_in_cookbook_cache
           @directory_root_in_cookbook_cache ||= begin
             cookbook = run_context.cookbook_collection[resource_cookbook]
             cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
           end
         end
   
         # Determine the cookbook to get the file from. If new resource sets an
         # explicit cookbook, use it, otherwise fall back to the implicit cookbook
         # i.e., the cookbook the resource was declared in.
         def resource_cookbook
           @new_resource.cookbook || @new_resource.cookbook_name
         end
   
         def create_cookbook_file(cookbook_file_relative_path)
           full_path = ::File.join(@new_resource.path, cookbook_file_relative_path)
   
           ensure_directory_exists(::File.dirname(full_path))
   
           file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path)
           if @new_resource.overwrite
             file_to_fetch.run_action(:create)
           else
             file_to_fetch.run_action(:create_if_missing)
           end
           @new_resource.updated_by_last_action(true) if file_to_fetch.updated?
         end
   
         def cookbook_file_resource(target_path, relative_source_path)
           cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context)
           cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
           cookbook_file.source(::File.join(@new_resource.source, relative_source_path))
           cookbook_file.mode(@new_resource.files_mode)    if @new_resource.files_mode
           cookbook_file.group(@new_resource.files_group)  if @new_resource.files_group
           cookbook_file.owner(@new_resource.files_owner)  if @new_resource.files_owner
           cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup
   
           cookbook_file
         end
   
         def ensure_directory_exists(path)
           unless ::File.directory?(path)
             directory_to_create = resource_for_directory(path)
             directory_to_create.run_action(:create)
             @new_resource.updated_by_last_action(true) if directory_to_create.updated?
           end
         end
   
         def resource_for_directory(path)
           dir = Chef::Resource::Directory.new(path, run_context)
           dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
           dir.mode(@new_resource.mode)
           dir.group(@new_resource.group)
           dir.owner(@new_resource.owner)
           dir.recursive(true)
           dir
         end
   
       end
     end
   end

lib/chef/role.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/couchdb'
   require 'chef/run_list'
   require 'chef/index_queue'
   require 'chef/mash'
   require 'chef/json_compat'
   require 'chef/search/query'
   
   class Chef
     class Role
   
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       include Chef::IndexQueue::Indexable
   
       DESIGN_DOCUMENT = {
         "version" => 6,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "role") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "role") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           }
         }
       }
   
       attr_accessor :couchdb_rev, :couchdb
       attr_reader :couchdb_id
   
       # Create a new Chef::Role object.
       def initialize(couchdb=nil)
         @name = ''
         @description = ''
         @default_attributes = Mash.new
         @override_attributes = Mash.new
         @env_run_lists = {"_default" => Chef::RunList.new}
         @couchdb_rev = nil
         @couchdb_id = nil
         @couchdb = couchdb || Chef::CouchDB.new
       end
   
       def couchdb_id=(value)
         @couchdb_id = value
         self.index_id = value
       end
   
       def chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def self.chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def name(arg=nil)
         set_or_return(
           :name,
           arg,
           :regex => /^[\-[:alnum:]_]+$/
         )
       end
   
       def description(arg=nil)
         set_or_return(
           :description,
           arg,
           :kind_of => String
         )
       end
   
       def run_list(*args)
         if (args.length > 0)
           @env_run_lists["_default"].reset!(args)
         end
         @env_run_lists["_default"]
       end
   
       alias_method :recipes, :run_list
   
       # For run_list expansion
       def run_list_for(environment)
         if env_run_lists[environment].nil?
           env_run_lists["_default"]
         else
           env_run_lists[environment]
         end
       end
   
       def active_run_list_for(environment)
         @env_run_lists.has_key?(environment) ? environment : '_default'
       end
   
       # Per environment run lists
       def env_run_lists(env_run_lists=nil)
         if (!env_run_lists.nil?)
           unless env_run_lists.key?("_default")
             msg = "_default key is required in env_run_lists.\n"
             msg << "(env_run_lists: #{env_run_lists.inspect})"
             raise Chef::Exceptions::InvalidEnvironmentRunListSpecification, msg
           end
           @env_run_lists.clear
           env_run_lists.each { |k,v| @env_run_lists[k] = Chef::RunList.new(*Array(v))}
         end
         @env_run_lists
       end
   
       alias :env_run_list :env_run_lists
   
       def default_attributes(arg=nil)
         set_or_return(
           :default_attributes,
           arg,
           :kind_of => Hash
         )
       end
   
       def override_attributes(arg=nil)
         set_or_return(
           :override_attributes,
           arg,
           :kind_of => Hash
         )
       end
   
       def to_hash
         env_run_lists_without_default = @env_run_lists.dup
         env_run_lists_without_default.delete("_default")
         result = {
           "name" => @name,
           "description" => @description,
           'json_class' => self.class.name,
           "default_attributes" => @default_attributes,
           "override_attributes" => @override_attributes,
           "chef_type" => "role",
           "run_list" => run_list,
           "env_run_lists" => env_run_lists_without_default
         }
         result["_rev"] = couchdb_rev if couchdb_rev
         result
       end
   
       # Serialize this object as a hash
       def to_json(*a)
         to_hash.to_json(*a)
       end
   
       def update_from!(o)
         description(o.description)
         recipes(o.recipes) if defined?(o.recipes)
         default_attributes(o.default_attributes)
         override_attributes(o.override_attributes)
         env_run_lists(o.env_run_lists) unless o.env_run_lists.nil?
         self
       end
   
       # Create a Chef::Role from JSON
       def self.json_create(o)
         role = new
         role.name(o["name"])
         role.description(o["description"])
         role.default_attributes(o["default_attributes"])
         role.override_attributes(o["override_attributes"])
   
         # _default run_list is in 'run_list' for newer clients, and
         # 'recipes' for older clients.
         env_run_list_hash = {"_default" => (o.has_key?("run_list") ? o["run_list"] : o["recipes"])}
   
         # Clients before 0.10 do not include env_run_lists, so only
         # merge if it's there.
         if o["env_run_lists"]
           env_run_list_hash.merge!(o["env_run_lists"])
         end
         role.env_run_lists(env_run_list_hash)
   
         role.couchdb_rev = o["_rev"] if o.has_key?("_rev")
         role.index_id = role.couchdb_id
         role.couchdb_id = o["_id"] if o.has_key?("_id")
         role
       end
   
       # List all the Chef::Role objects in the CouchDB.  If inflate is set to true, you will get
       # the full list of all Roles, fully inflated.
       def self.cdb_list(inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).list("roles", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }
       end
   
       # Get the list of all roles from the API.
       def self.list(inflate=false)
         if inflate
           response = Hash.new
           Chef::Search::Query.new.search(:role) do |n|
             response[n.name] = n unless n.nil?
           end
           response
         else
           chef_server_rest.get_rest("roles")
         end
       end
   
       # Load a role by name from CouchDB
       def self.cdb_load(name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("role", name)
       end
   
       # Load a role by name from the API
       def self.load(name)
         chef_server_rest.get_rest("roles/#{name}")
       end
   
       def self.exists?(rolename, couchdb)
         begin
           self.cdb_load(rolename, couchdb)
         rescue Chef::Exceptions::CouchDBNotFound
           nil
         end
       end
   
       def environment(env_name)
         chef_server_rest.get_rest("roles/#{@name}/environments/#{env_name}")
       end
   
       def environments
         chef_server_rest.get_rest("roles/#{@name}/environments")
       end
   
       # Remove this role from the CouchDB
       def cdb_destroy
         couchdb.delete("role", @name, couchdb_rev)
       end
   
       # Remove this role via the REST API
       def destroy
         chef_server_rest.delete_rest("roles/#{@name}")
       end
   
       # Save this role to the CouchDB
       def cdb_save
         self.couchdb_rev = couchdb.store("role", @name, self)["rev"]
       end
   
       # Save this role via the REST API
       def save
         begin
           chef_server_rest.put_rest("roles/#{@name}", self)
         rescue Net::HTTPServerException => e
           raise e unless e.response.code == "404"
           chef_server_rest.post_rest("roles", self)
         end
         self
       end
   
       # Create the role via the REST API
       def create
         chef_server_rest.post_rest("roles", self)
         self
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("roles", DESIGN_DOCUMENT)
       end
   
       # As a string
       def to_s
         "role[#{@name}]"
       end
   
       # Load a role from disk - prefers to load the JSON, but will happily load
       # the raw rb files as well.
       def self.from_disk(name, force=nil)
         js_file = File.join(Chef::Config[:role_path], "#{name}.json")
         rb_file = File.join(Chef::Config[:role_path], "#{name}.rb")
   
         if File.exists?(js_file) || force == "json"
           # from_json returns object.class => json_class in the JSON.
           Chef::JSONCompat.from_json(IO.read(js_file))
         elsif File.exists?(rb_file) || force == "ruby"
           role = Chef::Role.new
           role.name(name)
           role.from_file(rb_file)
           role
         else
           raise Chef::Exceptions::RoleNotFound, "Role '#{name}' could not be loaded from disk"
         end
       end
   
       # Sync all the json roles with couchdb from disk
       def self.sync_from_disk_to_couchdb
         Dir[File.join(Chef::Config[:role_path], "*.json")].each do |role_file|
           short_name = File.basename(role_file, ".json")
           Chef::Log.warn("Loading #{short_name}")
           r = Chef::Role.from_disk(short_name, "json")
           begin
             couch_role = Chef::Role.cdb_load(short_name)
             r.couchdb_rev = couch_role.couchdb_rev
             Chef::Log.debug("Replacing role #{short_name} with data from #{role_file}")
           rescue Chef::Exceptions::CouchDBNotFound
             Chef::Log.debug("Creating role #{short_name} with data from #{role_file}")
           end
           r.cdb_save
         end
       end
   
     end
   end

lib/chef/provider/mount.rb

   #
   # Author:: Joshua Timberman ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/command'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Mount < Chef::Provider
   
         include Chef::Mixin::Command
   
         def action_mount
           unless @current_resource.mounted
             status = mount_fs()
             if status
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} mounted")
             end
           else
             Chef::Log.debug("#{@new_resource} is already mounted")
           end
         end
   
         def action_umount
           if @current_resource.mounted
             status = umount_fs()
             if status
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} unmounted")
             end
           else
             Chef::Log.debug("#{@new_resource} is already unmounted")
           end
         end
   
         def action_remount
           unless @new_resource.supports[:remount]
             raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remount"
           else
             if @current_resource.mounted
               status = remount_fs()
               if status
                 @new_resource.updated_by_last_action(true)
                 Chef::Log.info("#{@new_resource} remounted")
               end
             else
               Chef::Log.debug("#{@new_resource} not mounted, nothing to remount")
             end
           end
         end
         
         def action_enable
           unless @current_resource.enabled
             status = enable_fs
             if status
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} enabled")
             else
               Chef::Log.debug("#{@new_resource} already enabled")
             end
           end
         end
         
         def action_disable
           if @current_resource.enabled
             status = disable_fs
             if status
               @new_resource.updated_by_last_action(true)            
               Chef::Log.info("#{@new_resource} disabled")
             else
               Chef::Log.debug("#{@new_resource} already disabled")
             end
           end
         end
   
         def mount_fs
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :mount"
         end
   
         def umount_fs
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :umount"
         end
   
         def remount_fs
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remount"
         end
         
         def enable_fs
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"        
         end
         
         def disable_fs
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"        
         end      
       end
     end
   end

lib/chef/provider/env.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider'
   require 'chef/mixin/command'
   require 'chef/resource/env'
   
   class Chef
     class Provider
       class Env < Chef::Provider
         include Chef::Mixin::Command
         attr_accessor :key_exists
   
         def initialize(new_resource, run_context)
           super
           @key_exists = true
         end
   
         def load_current_resource
           @current_resource = Chef::Resource::Env.new(@new_resource.name)
           @current_resource.key_name(@new_resource.key_name)
   
           if env_key_exists(@new_resource.key_name)
             @current_resource.value(env_value(@new_resource.key_name))
           else
             @key_exists = false
             Chef::Log.debug("#{@new_resource} key does not exist")
           end
   
           @current_resource
         end
   
         def env_value(key_name)
           raise Chef::Exceptions::Env, "#{self.to_s} provider does not implement env_value!"
         end
   
         def env_key_exists(key_name)
           env_value(key_name) ? true : false
         end
   
         # Check to see if value needs any changes
         #
         # ==== Returns
         # :: If a change is required
         # :: If a change is not required
         def compare_value
           if @new_resource.delim
             #e.g. check for existing value within PATH
             not @current_resource.value.split(@new_resource.delim).any? do |val|
               val == @new_resource.value
             end
           else
             @new_resource.value != @current_resource.value
           end
         end
   
         def action_create
           if @key_exists
             if compare_value
               modify_env
               Chef::Log.info("#{@new_resource} altered")
               @new_resource.updated_by_last_action(true)
             end
           else
             create_env
             Chef::Log.info("#{@new_resource} created")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         #e.g. delete a PATH element
         #
         # ==== Returns
         # :: If we handled the element case and caller should not delete the key
         # :: Caller should delete the key, either no :delim was specific or value was empty
         #           after we removed the element.
         def delete_element
           return false unless @new_resource.delim #no delim: delete the key
           if compare_value
             Chef::Log.debug("#{@new_resource} element '#{@new_resource.value}' does not exist")
             return true #do not delete the key
           else
             new_value =
               @current_resource.value.split(@new_resource.delim).select { |item|
               item != @new_resource.value
             }.join(@new_resource.delim)
   
             if new_value.empty?
               return false #nothing left here, delete the key
             else
               old_value = @new_resource.value(new_value)
               create_env
               Chef::Log.debug("#{@new_resource} deleted #{old_value} element")
               @new_resource.updated_by_last_action(true)
               return true #we removed the element and updated; do not delete the key
             end
           end
         end
   
         def action_delete
           if @key_exists && !delete_element
             delete_env
             Chef::Log.info("#{@new_resource} deleted")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def action_modify
           if @key_exists
             if compare_value
               modify_env
               Chef::Log.info("#{@new_resource} modified")
               @new_resource.updated_by_last_action(true)
             end
           else
             raise Chef::Exceptions::Env, "Cannot modify #{@new_resource} - key does not exist!"
           end
         end
   
         def create_env
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :#{@new_resource.action}"
         end
   
         def delete_env
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :delete"
         end
   
         def modify_env
           if @new_resource.delim
             #e.g. add to PATH
             @new_resource.value << @new_resource.delim << @current_resource.value
           end
           create_env
         end
        end
     end
   end

lib/chef/run_list/run_list_item.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     class RunList
       class RunListItem
         QUALIFIED_RECIPE             = %r{^recipe\[([^\]@]+)(@([0-9]+(\.[0-9]+){1,2}))?\]$}
         QUALIFIED_ROLE               = %r{^role\[([^\]]+)\]$}
         VERSIONED_UNQUALIFIED_RECIPE = %r{^([^@]+)(@([0-9]+(\.[0-9]+){1,2}))$}
   
         attr_reader :name, :type, :version
   
   
         def initialize(item)
           @version = nil
           case item
           when Hash
             assert_hash_is_valid_run_list_item!(item)
             @type = (item['type'] || item[:type]).to_sym
             @name = item['name'] || item[:name]
             if (item.has_key?('version') || item.has_key?(:version))
               @version = item['version'] || item[:version]
             end
           when String
             if match = QUALIFIED_RECIPE.match(item)
               # recipe[recipe_name]
               # recipe[recipe_name@1.0.0]
               @type = :recipe
               @name = match[1]
               @version = match[3] if match[3]
             elsif match = QUALIFIED_ROLE.match(item)
               # role[role_name]
               @type = :role
               @name = match[1]
             elsif match = VERSIONED_UNQUALIFIED_RECIPE.match(item)
               # recipe_name@1.0.0
               @type = :recipe
               @name = match[1]
               @version = match[3] if match[3]
             else
               # recipe_name
               @type = :recipe
               @name = item
             end
           else
             raise ArgumentError, "Unable to create #{self.class} from #{item.class}:#{item.inspect}: must be a Hash or String"
           end
         end
   
         def to_s
           "#{@type}[#{@name}#{@version ? "@#{@version}" :""}]"
         end
   
         def role?
           @type == :role
         end
   
         def recipe?
           @type == :recipe
         end
   
         def ==(other)
           if other.kind_of?(String)
             self.to_s == other.to_s
           else
             other.respond_to?(:type) && other.respond_to?(:name) && other.respond_to?(:version) && other.type == @type && other.name == @name && other.version == @version
           end
         end
   
         def assert_hash_is_valid_run_list_item!(item)
           unless (item.key?('type')|| item.key?(:type)) && (item.key?('name') || item.key?(:name))
             raise ArgumentError, "Initializing a #{self.class} from a hash requires that it have a 'type' and 'name' key"
           end
         end
   
       end
     end
   end

lib/chef/knife/core/text_formatter.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Knife
       module Core
         class TextFormatter
   
           attr_reader :data
           attr_reader :ui
   
           def initialize(data, ui)
             @ui = ui
             @data = if data.respond_to?(:display_hash)
               data.display_hash
             elsif data.kind_of?(Array)
               data
             elsif data.respond_to?(:to_hash)
               data.to_hash
             else
               data
             end
           end
   
           def formatted_data
             @formatted_data ||= text_format(data)
           end
   
           def text_format(data, indent=0)
             buffer = ''
   
             if data.respond_to?(:keys)
               justify_width = data.keys.map {|k| k.to_s.size }.max.to_i + 2
               data.sort.each do |key, value|
                 justified_key = ui.color("#{key}:".ljust(justify_width), :cyan)
                 if should_enumerate?(value)
                   buffer << indent_line(justified_key, indent)
                   buffer << text_format(value, indent + 1)
                 else
                   buffer << indent_key_value(justified_key, value, justify_width, indent)
                 end
               end
             elsif data.kind_of?(Array)
               data.each do |item|
                 if should_enumerate?(data)
                   buffer << text_format(item, indent + 1)
                 else
                   buffer << indent_line(item, indent)
                 end
               end
             else
               buffer << indent_line(stringify_value(data), indent)
             end
             buffer
           end
   
           # Ruby 1.8 Strings include enumberable, which is not what we want. So
           # we have this heuristic to detect hashes and arrays instead.
           def should_enumerate?(value)
             ((value.respond_to?(:keys) && !value.empty? ) || ( value.kind_of?(Array) && (value.size > 1 || should_enumerate?(value.first) )))
           end
   
           def indent_line(string, indent)
             ("  " * indent) << "#{string}\n"
           end
   
           def indent_key_value(key, value, justify_width, indent)
             lines = value.to_s.split("\n")
             if lines.size > 1
               total_indent = (2 * indent) + justify_width + 1
               indent_line("#{key} #{lines.shift}", indent) << lines.map {|l| (" " * total_indent) + l << "\n" }.join("")
             else
               indent_line("#{key} #{stringify_value(value)}", indent)
             end
           end
   
           def stringify_value(data)
             data.kind_of?(String) ? data : data.to_s
           end
   
         end
       end
     end
   end
   

lib/chef/resource/deploy.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   # EX:
   # deploy "/my/deploy/dir" do
   #   repo "git@github.com/whoami/project"
   #   revision "abc123" # or "HEAD" or "TAG_for_1.0" or (subversion) "1234"
   #   user "deploy_ninja"
   #   enable_submodules true
   #   migrate true
   #   migration_command "rake db:migrate"
   #   environment "RAILS_ENV" => "production", "OTHER_ENV" => "foo"
   #   shallow_clone true
   #   action :deploy # or :rollback
   #   restart_command "touch tmp/restart.txt"
   #   git_ssh_wrapper "wrap-ssh4git.sh"
   #   scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion
   #   svn_username "whoami"
   #   svn_password "supersecret"
   # end
   
   require "chef/resource/scm"
   
   class Chef
     class Resource
   
       # Deploy: Deploy apps from a source control repository.
       #
       # Callbacks:
       # Callbacks can be a block or a string. If given a block, the code
       # is evaluated as an embedded recipe, and run at the specified
       # point in the deploy process. If given a string, the string is taken as
       # a path to a callback file/recipe. Paths are evaluated relative to the
       # release directory. Callback files can contain chef code (resources, etc.)
       #
       class Deploy < Chef::Resource
   
         provider_base Chef::Provider::Deploy
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :deploy
           @deploy_to = name
           @environment = nil
           @repository_cache = 'cached-copy'
           @copy_exclude = []
           @purge_before_symlink = %w{log tmp/pids public/system}
           @create_dirs_before_symlink = %w{tmp public config}
           @symlink_before_migrate = {"config/database.yml" => "config/database.yml"}
           @symlinks = {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
           @revision = 'HEAD'
           @action = :deploy
           @migrate = false
           @rollback_on_error = false
           @remote = "origin"
           @enable_submodules = false
           @shallow_clone = false
           @scm_provider = Chef::Provider::Git
           @svn_force_export = false
           @provider = Chef::Provider::Deploy::Timestamped
           @allowed_actions.push(:force_deploy, :deploy, :rollback)
           @additional_remotes = Hash[]
         end
   
         # where the checked out/cloned code goes
         def destination
           @destination ||= shared_path + "/#{@repository_cache}"
         end
   
         # where shared stuff goes, i.e., logs, tmp, etc. goes here
         def shared_path
           @shared_path ||= @deploy_to + "/shared"
         end
   
         # where the deployed version of your code goes
         def current_path
           @current_path ||= @deploy_to + "/current"
         end
   
         def depth
           @shallow_clone ? "5" : nil
         end
   
         # note: deploy_to is your application "meta-root."
         def deploy_to(arg=nil)
           set_or_return(
             :deploy_to,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def repo(arg=nil)
           set_or_return(
             :repo,
             arg,
             :kind_of => [ String ]
           )
         end
         alias :repository :repo
   
         def remote(arg=nil)
           set_or_return(
             :remote,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def role(arg=nil)
           set_or_return(
             :role,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def restart_command(arg=nil, &block)
           arg ||= block
           set_or_return(
             :restart_command,
             arg,
             :kind_of => [ String, Proc ]
           )
         end
         alias :restart :restart_command
   
         def migrate(arg=nil)
           set_or_return(
             :migrate,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def migration_command(arg=nil)
           set_or_return(
             :migration_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def rollback_on_error(arg=nil)
           set_or_return(
             :rollback_on_error,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def user(arg=nil)
           set_or_return(
             :user,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def enable_submodules(arg=nil)
           set_or_return(
             :enable_submodules,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def shallow_clone(arg=nil)
           set_or_return(
             :shallow_clone,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def repository_cache(arg=nil)
           set_or_return(
             :repository_cache,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def copy_exclude(arg=nil)
           set_or_return(
             :copy_exclude,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def revision(arg=nil)
           set_or_return(
             :revision,
             arg,
             :kind_of => [ String ]
           )
         end
         alias :branch :revision
   
         def git_ssh_wrapper(arg=nil)
           set_or_return(
             :git_ssh_wrapper,
             arg,
             :kind_of => [ String ]
           )
         end
         alias :ssh_wrapper :git_ssh_wrapper
   
         def svn_username(arg=nil)
           set_or_return(
             :svn_username,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def svn_password(arg=nil)
           set_or_return(
             :svn_password,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def svn_arguments(arg=nil)
           set_or_return(
             :svn_arguments,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def svn_info_args(arg=nil)
           set_or_return(
             :svn_arguments,
             arg,
             :kind_of => [ String ])
         end
   
         def scm_provider(arg=nil)
           klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
                     lookup_provider_constant(arg)
                   else
                     arg
                   end
           set_or_return(
             :scm_provider,
             klass,
             :kind_of => [ Class ]
           )
         end
   
         def svn_force_export(arg=nil)
           set_or_return(
             :svn_force_export,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def environment(arg=nil)
           if arg.is_a?(String)
             Chef::Log.debug "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'"
             Chef::Log.warn "[DEPRECATED] please modify your deploy recipe or attributes to set the environment using a hash"
             arg = {"RAILS_ENV"=>arg,"MERB_ENV"=>arg,"RACK_ENV"=>arg}
           end
           set_or_return(
             :environment,
             arg,
             :kind_of => [ Hash ]
           )
         end
   
         # An array of paths, relative to your app's root, to be purged from a
         # SCM clone/checkout before symlinking. Use this to get rid of files and
         # directories you want to be shared between releases.
         # Default: ["log", "tmp/pids", "public/system"]
         def purge_before_symlink(arg=nil)
           set_or_return(
             :purge_before_symlink,
             arg,
             :kind_of => Array
           )
         end
   
         # An array of paths, relative to your app's root, where you expect dirs to
         # exist before symlinking. This runs after #purge_before_symlink, so you
         # can use this to recreate dirs that you had previously purged.
         # For example, if you plan to use a shared directory for pids, and you
         # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
         # then specify tmp here so that the tmp directory will exist when you
         # symlink the pids directory in to the current release.
         # Default: ["tmp", "public", "config"]
         def create_dirs_before_symlink(arg=nil)
           set_or_return(
             :create_dirs_before_symlink,
             arg,
             :kind_of => Array
           )
         end
   
         # A Hash of shared/dir/path => release/dir/path. This attribute determines
         # which files and dirs in the shared directory get symlinked to the current
         # release directory, and where they go. If you have a directory
         # $shared/pids that you would like to symlink as $current_release/tmp/pids
         # you specify it as "pids" => "tmp/pids"
         # Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
         def symlinks(arg=nil)
           set_or_return(
             :symlinks,
             arg,
             :kind_of => Hash
           )
         end
   
         # A Hash of shared/dir/path => release/dir/path. This attribute determines
         # which files in the shared directory get symlinked to the current release
         # directory and where they go. Unlike map_shared_files, these are symlinked
         # *before* any migration is run.
         # For a rails/merb app, this is used to link in a known good database.yml
         # (with the production db password) before running migrate.
         # Default {"config/database.yml" => "config/database.yml"}
         def symlink_before_migrate(arg=nil)
           set_or_return(
             :symlink_before_migrate,
             arg,
             :kind_of => Hash
           )
         end
   
         # Callback fires before migration is run.
         def before_migrate(arg=nil, &block)
           arg ||= block
           set_or_return(:before_migrate, arg, :kind_of => [Proc, String])
         end
   
         # Callback fires before symlinking
         def before_symlink(arg=nil, &block)
           arg ||= block
           set_or_return(:before_symlink, arg, :kind_of => [Proc, String])
         end
   
         # Callback fires before restart
         def before_restart(arg=nil, &block)
           arg ||= block
           set_or_return(:before_restart, arg, :kind_of => [Proc, String])
         end
   
         # Callback fires after restart
         def after_restart(arg=nil, &block)
           arg ||= block
           set_or_return(:after_restart, arg, :kind_of => [Proc, String])
         end
         
         def additional_remotes(arg=nil)
           set_or_return(
             :additional_remotes,
             arg,
             :kind_of => Hash
           )
         end
   
       end
     end
   end

lib/chef/provider/mdadm.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/mixin/shell_out'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Mdadm < Chef::Provider
   
         #include Chef::Mixin::Command
         include Chef::Mixin::ShellOut
         
         def popen4
           raise Exception, "deprecated, bitches"
         end
   
         def load_current_resource
           @current_resource = Chef::Resource::Mdadm.new(@new_resource.name)
           @current_resource.raid_device(@new_resource.raid_device)
           Chef::Log.debug("#{@new_resource} checking for software raid device #{@current_resource.raid_device}")
   
           mdadm = shell_out!("mdadm --detail --scan")
           exists = mdadm.stdout.include?(@new_resource.raid_device)
           #exists = false
           # popen4(command) do |pid, stdin, stdout, stderr|
           #   stdout.each do |line|
           #     if line.include? @new_resource.raid_device
           #       exists = true
           #     end
           #   end
           # end
           @current_resource.exists(exists)
         end
   
         def action_create
           unless @current_resource.exists
             command = "yes | mdadm --create #{@new_resource.raid_device} --chunk=#{@new_resource.chunk} --level #{@new_resource.level} --raid-devices #{@new_resource.devices.length} #{@new_resource.devices.join(" ")}"
             Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
             #pid, stdin, stdout, stderr = popen4(command)
             shell_out!(command)
             Chef::Log.info("#{@new_resource} created raid device (#{@new_resource.raid_device})")
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug("#{@new_resource} raid device already exists, skipping create (#{@new_resource.raid_device})")
           end
         end
   
         def action_assemble
           unless @current_resource.exists
             command = "yes | mdadm --assemble #{@new_resource.raid_device} #{@new_resource.devices.join(" ")}"
             Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
             shell_out!(command)
             Chef::Log.info("#{@new_resource} assembled raid device (#{@new_resource.raid_device})")
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug("#{@new_resource} raid device already exists, skipping assemble (#{@new_resource.raid_device})")
           end
         end
   
         def action_stop
           if @current_resource.exists
             command = "yes | mdadm --stop #{@new_resource.raid_device}"
             Chef::Log.debug("#{@new_resource} mdadm command: #{command}")
             shell_out!(command)
             Chef::Log.info("#{@new_resource} stopped raid device (#{@new_resource.raid_device})")
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug("#{@new_resource} raid device doesn't exist (#{@new_resource.raid_device}) - not stopping")
           end
         end
   
       end
     end
   end

lib/chef/provider/package/freebsd.rb

   #
   # Authors:: Bryan McLellan (btm@loftninjas.org)
   #           Matthew Landauer (matthew@openaustralia.org)
   # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/shell_out'
   require 'chef/resource/package'
   require 'chef/mixin/get_source_from_package'
   
   class Chef
     class Provider
       class Package
         class Freebsd < Chef::Provider::Package
           include Chef::Mixin::ShellOut
   
           include Chef::Mixin::GetSourceFromPackage
   
           def initialize(*args)
             super
             @current_resource = Chef::Resource::Package.new(@new_resource.name)
           end
   
           def current_installed_version
             pkg_info = shell_out!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0,1])
             pkg_info.stdout[/^#{package_name}-(.+)/, 1]
           end
   
           def port_path
             case @new_resource.package_name
             # When the package name starts with a '/' treat it as the full path to the ports directory
             when /^\//
               @new_resource.package_name
             # Otherwise if the package name contains a '/' not at the start (like 'www/wordpress') treat as a relative
             # path from /usr/ports
             when /\//
               "/usr/ports/#{@new_resource.package_name}"
             # Otherwise look up the path to the ports directory using 'whereis'
             else
               whereis = shell_out!("whereis -s #{@new_resource.package_name}", :env => nil)
               unless path = whereis.stdout[/^#{@new_resource.package_name}:\s+(.+)$/, 1]
                 raise Chef::Exceptions::Package, "Could not find port with the name #{@new_resource.package_name}"
               end
               path
             end
           end
   
           def ports_makefile_variable_value(variable)
             make_v = shell_out!("make -V #{variable} -f #{port_path}/Makefile", :env => nil, :returns => [0,1])
             make_v.stdout.strip.split($\).first # $\ is the line separator, i.e., newline
           end
   
           def ports_candidate_version
             ports_makefile_variable_value("PORTVERSION")
           end
   
           def load_current_resource
             @current_resource.package_name(@new_resource.package_name)
   
             @current_resource.version(current_installed_version)
             Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version
   
             @candidate_version = ports_candidate_version
             Chef::Log.debug("#{@new_resource} ports candidate version is #{@candidate_version}") if @candidate_version
   
             @current_resource
           end
   
           def latest_link_name
             ports_makefile_variable_value("LATEST_LINK")
           end
   
           # The name of the package (without the version number) as understood by pkg_add and pkg_info
           def package_name
             if ports_makefile_variable_value("PKGNAME") =~ /^(.+)-[^-]+$/
               $1
             else
               raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
             end
           end
   
           def install_package(name, version)
             unless @current_resource.version
               case @new_resource.source
               when /^ports$/
                 shell_out!("make -DBATCH -f #{port_path}/Makefile install", :timeout => 1200, :env => nil).status
               when /^http/, /^ftp/
                 shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, 'LC_ALL' => nil }).status
                 Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
               when /^\//
                 shell_out!("pkg_add #{@new_resource.name}", :env => { "PKG_PATH" => @new_resource.source , 'LC_ALL'=>nil}).status
                 Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
               else
                 shell_out!("pkg_add -r #{latest_link_name}", :env => nil).status
               end
             end
           end
   
           def remove_package(name, version)
             # a version is mandatory
             if version
               shell_out!("pkg_delete #{package_name}-#{version}", :env => nil).status
             else
               shell_out!("pkg_delete #{package_name}-#{@current_resource.version}", :env => nil).status
             end
           end
   
         end
       end
     end
   end

lib/chef/resource/service.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Service < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :service
           @service_name = name
           @enabled = nil
           @running = nil
           @pattern = service_name 
           @start_command = nil
           @stop_command = nil
           @status_command = nil
           @restart_command = nil
           @reload_command = nil
           @priority = nil
           @action = "nothing"
           @startup_type = :automatic
           @supports = { :restart => false, :reload => false, :status => false }
           @allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload)
         end
         
         def service_name(arg=nil)
           set_or_return(
             :service_name,
             arg,
             :kind_of => [ String ]
           )
         end
         
         # regex for match against ps -ef when !supports[:has_status] && status == nil
         def pattern(arg=nil)
           set_or_return(
             :pattern,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # command to call to start service
         def start_command(arg=nil)
           set_or_return(
             :start_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # command to call to stop service
         def stop_command(arg=nil)
           set_or_return(
             :stop_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # command to call to get status of service
         def status_command(arg=nil)
           set_or_return(
             :status_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # command to call to restart service
         def restart_command(arg=nil)
           set_or_return(
             :restart_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def reload_command(arg=nil)
           set_or_return(
             :reload_command,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # if the service is enabled or not
         def enabled(arg=nil)
           set_or_return(
             :enabled,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         # if the service is running or not
         def running(arg=nil)
           set_or_return(
             :running,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         # Priority arguments can have two forms:
         #
         # - a simple number, in which the default start runlevels get
         #   that as the start value and stop runlevels get 100 - value.
         #
         # - a hash like { 2 => [:start, 20], 3 => [:stop, 55] }, where
         #   the service will be marked as started with priority 20 in
         #   runlevel 2, stopped in 3 with priority 55 and no symlinks or
         #   similar for other runlevels
         #
         def priority(arg=nil)
           set_or_return(:priority,
                         arg,
                         :kind_of => [ Integer, String, Hash ])
         end
   
         def supports(args={})
           if args.is_a? Array
             args.each { |arg| @supports[arg] = true }
           elsif args.any?
             @supports = args
           else
             @supports
           end
         end
   
       end
     end
   end

lib/chef/provider/user/windows.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/user'
   if RUBY_PLATFORM =~ /mswin|mingw32|windows/
     require 'chef/util/windows/net_user'
   end
   
   class Chef
     class Provider
       class User
         class Windows < Chef::Provider::User
   
           def initialize(new_resource,run_context)
             super
             @net_user = Chef::Util::Windows::NetUser.new(@new_resource.name)
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::User.new(@new_resource.name)
             @current_resource.username(@new_resource.username)
             user_info = nil
             begin
               user_info = @net_user.get_info
             rescue
               @user_exists = false
               Chef::Log.debug("#{@new_resource} does not exist")
             end
   
             if user_info
               @current_resource.uid(user_info[:user_id])
               @current_resource.gid(user_info[:primary_group_id])
               @current_resource.comment(user_info[:full_name])
               @current_resource.home(user_info[:home_dir])
               @current_resource.shell(user_info[:script_path])
             end
   
             @current_resource
           end
   
           # Check to see if the user needs any changes
           #
           # === Returns
           # :: If a change is required
           # :: If the users are identical
           def compare_user
             unless @net_user.validate_credentials(@new_resource.password)
               Chef::Log.debug("#{@new_resource} password has changed")
               return true
             end
             [ :uid, :gid, :comment, :home, :shell ].any? do |user_attrib|
               !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
             end
           end
   
           def create_user
             @net_user.add(set_options)
           end
           
           def manage_user
             @net_user.update(set_options)
           end
           
           def remove_user
             @net_user.delete
           end
           
           def check_lock
             @net_user.check_enabled
           end
           
           def lock_user
             @net_user.disable_account
           end
           
           def unlock_user
             @net_user.enable_account
           end
   
           def set_options
             opts = {:name => @new_resource.username}
   
             field_list = {
               'comment' => 'full_name',
               'home' => 'home_dir',
               'gid' => 'primary_group_id',
               'uid' => 'user_id',
               'shell' => 'script_path',
               'password' => 'password'
             }
   
             field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option|
               field_symbol = field.to_sym
               if @current_resource.send(field_symbol) != @new_resource.send(field_symbol)
                 if @new_resource.send(field_symbol)
                   unless field_symbol == :password
                     Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}")
                   end
                   opts[option.to_sym] = @new_resource.send(field_symbol)
                 end
               end
             end
             opts
           end
   
         end
       end
     end
   end

lib/chef/provider/group/dscl.rb

   #
   # Author:: Dreamcat4 ()
   # Copyright:: Copyright (c) 2009 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Provider
       class Group
         class Dscl < Chef::Provider::Group
   
           def dscl(*args)
             host = "."
             stdout_result = ""; stderr_result = ""; cmd = "dscl #{host} -#{args.join(' ')}"
             status = popen4(cmd) do |pid, stdin, stdout, stderr|
               stdout.each { |line| stdout_result << line }
               stderr.each { |line| stderr_result << line }
             end
             return [cmd, status, stdout_result, stderr_result]
           end
   
           def safe_dscl(*args)
             result = dscl(*args)
             return "" if ( args.first =~ /^delete/ ) && ( result[1].exitstatus != 0 )
             raise(Chef::Exceptions::Group,"dscl error: #{result.inspect}") unless result[1].exitstatus == 0
             raise(Chef::Exceptions::Group,"dscl error: #{result.inspect}") if result[2] =~ /No such key: /
             return result[2]
           end
           
           # This is handled in providers/group.rb by Etc.getgrnam()
           # def group_exists?(group)
           #   groups = safe_dscl("list /Groups")
           #   !! ( groups =~ Regexp.new("\n#{group}\n") )
           # end
   
           # get a free GID greater than 200
           def get_free_gid(search_limit=1000)
             gid = nil; next_gid_guess = 200
             groups_gids = safe_dscl("list /Groups gid")
             while(next_gid_guess < search_limit + 200)
               if groups_gids =~ Regexp.new("#{Regexp.escape(next_gid_guess.to_s)}\n")
                 next_gid_guess += 1
               else
                 gid = next_gid_guess
                 break
               end
             end
             return gid || raise("gid not found. Exhausted. Searched #{search_limit} times")
           end
   
           def gid_used?(gid)
             return false unless gid
             groups_gids = safe_dscl("list /Groups gid")
                ( groups_gids =~ Regexp.new("#{Regexp.escape(gid.to_s)}\n") )
           end
   
           def set_gid
             @new_resource.gid(get_free_gid) if [nil,""].include? @new_resource.gid
             raise(Chef::Exceptions::Group,"gid is already in use") if gid_used?(@new_resource.gid)
             safe_dscl("create /Groups/#{@new_resource.group_name} PrimaryGroupID #{@new_resource.gid}")
           end
   
           def set_members
             unless @new_resource.append
               Chef::Log.debug("#{@new_resource} removing group members #{@current_resource.members.join(' ')}") unless @current_resource.members.empty?
               safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers ''") # clear guid list
               safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership ''") # clear user list
             end
             unless @new_resource.members.empty?
               Chef::Log.debug("#{@new_resource} setting group members #{@new_resource.members.join(', ')}")
               safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{@new_resource.members.join(' ')}")
             end
           end
   
           def load_current_resource
             super
             raise Chef::Exceptions::Group, "Could not find binary /usr/bin/dscl for #{@new_resource}" unless ::File.exists?("/usr/bin/dscl")
           end
           
           def create_group
             dscl_create_group
             set_gid
             set_members
           end
           
           def manage_group
             if @new_resource.group_name && (@current_resource.group_name != @new_resource.group_name)
               dscl_create_group
             end
             if @new_resource.gid && (@current_resource.gid != @new_resource.gid)
               set_gid
             end
             if @new_resource.members && (@current_resource.members != @new_resource.members)
               set_members
             end
           end
           
           def dscl_create_group
             safe_dscl("create /Groups/#{@new_resource.group_name}")
             safe_dscl("create /Groups/#{@new_resource.group_name} Password '*'")
           end
           
           def remove_group
             safe_dscl("delete /Groups/#{@new_resource.group_name}")
           end
         end
       end
     end
   end

lib/chef/mixin/deep_merge.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Steve Midgley (http://www.misuse.org/science)
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # Copyright:: Copyright (c) 2008 Steve Midgley
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     module Mixin
       # == Chef::Mixin::DeepMerge
       # Implements a deep merging algorithm for nested data structures.
       # ==== Notice:
       #   This code is imported from deep_merge by Steve Midgley. deep_merge is
       #   available under the MIT license from
       #   http://trac.misuse.org/science/wiki/DeepMerge
       module DeepMerge
         def self.merge(first, second)
           first  = Mash.new(first)  unless first.kind_of?(Mash)
           second = Mash.new(second) unless second.kind_of?(Mash)
   
           DeepMerge.deep_merge!(second, first, {:knockout_prefix => "!merge:", :preserve_unmergeables => false})
         end
       
         class InvalidParameter < StandardError; end
         
         DEFAULT_FIELD_KNOCKOUT_PREFIX = '--' unless defined?(DEFAULT_FIELD_KNOCKOUT_PREFIX)
        
         # Deep Merge core documentation.
         # deep_merge! method permits merging of arbitrary child elements. The two top level
         # elements must be hashes. These hashes can contain unlimited (to stack limit) levels
         # of child elements. These child elements to not have to be of the same types.
         # Where child elements are of the same type, deep_merge will attempt to merge them together.
         # Where child elements are not of the same type, deep_merge will skip or optionally overwrite
         # the destination element with the contents of the source element at that level.
         # So if you have two hashes like this:
         #   source = {:x => [1,2,3], :y => 2}
         #   dest =   {:x => [4,5,'6'], :y => [7,8,9]}
         #   dest.deep_merge!(source)
         #   Results: {:x => [1,2,3,4,5,'6'], :y => 2}
         # By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
         # To avoid this, use "deep_merge" (no bang/exclamation mark)
         # 
         # Options:
         #   Options are specified in the last parameter passed, which should be in hash format:
         #   hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
         #   :preserve_unmergeables  DEFAULT: false
         #      Set to true to skip any unmergeable elements from source
         #   :knockout_prefix        DEFAULT: nil
         #      Set to string value to signify prefix which deletes elements from existing element
         #   :sort_merged_arrays     DEFAULT: false
         #      Set to true to sort all arrays that are merged together
         #   :unpack_arrays          DEFAULT: nil
         #      Set to string value to run "Array::join" then "String::split" against all arrays
         #   :merge_debug            DEFAULT: false
         #      Set to true to get console output of merge process for debugging
         #
         # Selected Options Details:
         # :knockout_prefix => The purpose of this is to provide a way to remove elements 
         #   from existing Hash by specifying them in a special way in incoming hash
         #    source = {:x => ['--1', '2']}
         #    dest   = {:x => ['1', '3']}
         #    dest.ko_deep_merge!(source)
         #    Results: {:x => ['2','3']}
         #   Additionally, if the knockout_prefix is passed alone as a string, it will cause
         #   the entire element to be removed:
         #    source = {:x => '--'}
         #    dest   = {:x => [1,2,3]}
         #    dest.ko_deep_merge!(source)
         #    Results: {:x => ""}
         # :unpack_arrays => The purpose of this is to permit compound elements to be passed
         #   in as strings and to be converted into discrete array elements
         #   irsource = {:x => ['1,2,3', '4']}
         #   dest   = {:x => ['5','6','7,8']}
         #   dest.deep_merge!(source, {:unpack_arrays => ','})
         #   Results: {:x => ['1','2','3','4','5','6','7','8'}
         #   Why: If receiving data from an HTML form, this makes it easy for a checkbox 
         #    to pass multiple values from within a single HTML element
         # 
         # There are many tests for this library - and you can learn more about the features
         # and usages of deep_merge! by just browsing the test examples
         def self.deep_merge!(source, dest, options = {})
           # turn on this line for stdout debugging text
           merge_debug = options[:merge_debug] || false
           overwrite_unmergeable = !options[:preserve_unmergeables]
           knockout_prefix = options[:knockout_prefix] || nil
           raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == ""
           raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable
           # if present: we will split and join arrays on this char before merging
           array_split_char = options[:unpack_arrays] || false
           # request that we sort together any arrays when they are merged
           sort_merged_arrays = options[:sort_merged_arrays] || false
           di = options[:debug_indent] || ''
           # do nothing if source is nil
           return dest if source.nil?
           # if dest doesn't exist, then simply copy source to it
           if dest.nil? && overwrite_unmergeable
             dest = source; return dest
           end
        
           puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
           if source.kind_of?(Hash)
             puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
             source.each do |src_key, src_value|
               if dest.kind_of?(Hash)
                 puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
                 if dest[src_key]
                   puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
                   dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + '  '))
                 else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
                   puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
                   # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
                   begin
                     src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
                   rescue TypeError
                     src_dup = src_value
                   end
                   dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + '  '))
                 end
               else # dest isn't a hash, so we overwrite it completely (if permitted)
                 if overwrite_unmergeable
                   puts "#{di}  overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over->  #{dest.inspect}" if merge_debug
                   dest = overwrite_unmergeables(source, dest, options)
                 end
               end
             end
           elsif source.kind_of?(Array)
             puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
             # if we are instructed, join/split any source arrays before processing
             if array_split_char
               puts "#{di} split/join on source: #{source.inspect}" if merge_debug
               source = source.join(array_split_char).split(array_split_char)
               if dest.kind_of?(Array)
                 dest = dest.join(array_split_char).split(array_split_char)
               end
             end
             # if there's a naked knockout_prefix in source, that means we are to truncate dest
             if source.index(knockout_prefix)
               dest = clear_or_nil(dest); source.delete(knockout_prefix)
             end
             if dest.kind_of?(Array)
               if knockout_prefix
                 print "#{di} knocking out: " if merge_debug
                 # remove knockout prefix items from both source and dest
                 source.delete_if do |ko_item|
                   retval = false
                   item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
                   if item != ko_item
                     print "#{ko_item} - " if merge_debug
                     dest.delete(item)
                     dest.delete(ko_item)
                     retval = true
                   end
                   retval
                 end
                 puts if merge_debug
               end
               puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
               dest = dest | source
               dest.sort! if sort_merged_arrays
             elsif overwrite_unmergeable
               puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
               dest = overwrite_unmergeables(source, dest, options)
             end
           else # src_hash is not an array or hash, so we'll have to overwrite dest
             puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
             dest = overwrite_unmergeables(source, dest, options)
           end
           puts "#{di}Returning #{dest.inspect}" if merge_debug
           dest
         end # deep_merge!
        
         # allows deep_merge! to uniformly handle overwriting of unmergeable entities
         def self.overwrite_unmergeables(source, dest, options)
           merge_debug = options[:merge_debug] || false
           overwrite_unmergeable = !options[:preserve_unmergeables]
           knockout_prefix = options[:knockout_prefix] || false
           di = options[:debug_indent] || ''
           if knockout_prefix && overwrite_unmergeable
             if source.kind_of?(String) # remove knockout string from source before overwriting dest
               src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
             elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
               src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
             else
               src_tmp = source
             end
             if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
               puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
               dest = src_tmp
             else # if we do find a knockout_prefix, then we just delete dest
               puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
               dest = ""
             end
           elsif overwrite_unmergeable
             dest = source
           end
           dest
         end
        
         def self.clear_or_nil(obj)
           if obj.respond_to?(:clear)
             obj.clear
           else
             obj = nil
           end
           obj
         end
        
       end
        
     end
   end
   
   

lib/chef/application.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'socket'
   require 'chef/config'
   require 'chef/exceptions'
   require 'chef/log'
   require 'mixlib/cli'
   require 'tmpdir'
   
   class Chef::Application
     include Mixlib::CLI
   
     class Wakeup < Exception
     end
   
     def initialize
       super
   
       trap("TERM") do
         Chef::Application.fatal!("SIGTERM received, stopping", 1)
       end
   
       trap("INT") do
         Chef::Application.fatal!("SIGINT received, stopping", 2)
       end
   
       unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
         trap("QUIT") do
           Chef::Log.info("SIGQUIT received, call stack:\n  " + caller.join("\n  "))
         end
   
         trap("HUP") do
           Chef::Log.info("SIGHUP received, reconfiguring")
           reconfigure
         end
       end
   
       # Always switch to a readable directory. Keeps subsequent Dir.chdir() {}
       # from failing due to permissions when launched as a less privileged user.
     end
   
     # Reconfigure the application. You'll want to override and super this method.
     def reconfigure
       configure_chef
       configure_logging
     end
   
     # Get this party started
     def run
       reconfigure
       setup_application
       run_application
     end
   
     # Parse the configuration file
     def configure_chef
       parse_options
   
       begin
         case config[:config_file]
         when /^(http|https):\/\//
           Chef::REST.new("", nil, nil).fetch(config[:config_file]) { |f| apply_config(f.path) }
         else
           ::File::open(config[:config_file]) { |f| apply_config(f.path) }
         end
       rescue SocketError => error
         Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", 2)
       rescue Exception => error
         Chef::Log.warn("*****************************************")
         Chef::Log.warn("Can not find config file: #{config[:config_file]}, using defaults.")
         Chef::Log.warn("#{error.message}")
         Chef::Log.warn("*****************************************")
   
         Chef::Config.merge!(config)
       end
   
     end
   
     # Initialize and configure the logger. If the configured log location is not
     # STDOUT, but stdout is a TTY and we're not daemonizing, we set up a secondary
     # logger with output to stdout. This way, we magically do the right thing when
     # the user has configured logging to a file but they're running chef in the
     # shell to debug something.
     def configure_logging
       Chef::Log.init(Chef::Config[:log_location])
       if ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && (!Chef::Config[:daemonize])
         stdout_logger = Logger.new(STDOUT)
         STDOUT.sync = true
         stdout_logger.formatter = Chef::Log.logger.formatter
         Chef::Log.loggers <<  stdout_logger
       end
       Chef::Log.level = Chef::Config[:log_level]
     end
   
     # Called prior to starting the application, by the run method
     def setup_application
       raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
     end
   
     # Actually run the application
     def run_application
       raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
     end
   
     private
   
     def apply_config(config_file_path)
       Chef::Config.from_file(config_file_path)
       Chef::Config.merge!(config)
     end
   
   
     class << self
       def debug_stacktrace(e)
         message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
         chef_stacktrace_out = "Generated at #{Time.now.to_s}\n"
         chef_stacktrace_out += message
   
         Chef::FileCache.store("chef-stacktrace.out", chef_stacktrace_out)
         Chef::Log.fatal("Stacktrace dumped to #{Chef::FileCache.load("chef-stacktrace.out", false)}")
         Chef::Log.debug(message)
         true
       end
   
       # Log a fatal error message to both STDERR and the Logger, exit the application
       def fatal!(msg, err = -1)
         Chef::Log.fatal(msg)
         Process.exit err
       end
   
       def exit!(msg, err = -1)
         Chef::Log.debug(msg)
         Process.exit err
       end
     end
   
   end

lib/chef/provider/service.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/command'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Service < Chef::Provider
   
         include Chef::Mixin::Command
   
         def initialize(new_resource, run_context)
           super
           @enabled = nil
         end
   
         def action_enable
           if @current_resource.enabled
             Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
           else
             if enable_service
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} enabled")
             end
           end
         end
   
         def action_disable
           if @current_resource.enabled
             if disable_service
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} disabled")
             end
           else
             Chef::Log.debug("#{@new_resource} already disabled - nothing to do")
           end
         end
   
         def action_start
           unless @current_resource.running
             if start_service
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} started")
             end
           else
             Chef::Log.debug("#{@new_resource} already running - nothing to do")
           end 
         end
   
         def action_stop
           if @current_resource.running
             if stop_service
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} stopped")
             end
           else
             Chef::Log.debug("#{@new_resource} already stopped - nothing to do")
           end 
         end
         
         def action_restart
           if restart_service
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} restarted")
           end
         end
   
         def action_reload
           unless (@new_resource.supports[:reload] || @new_resource.reload_command)
             raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
           end
           if @current_resource.running
             if reload_service
               @new_resource.updated_by_last_action(true)
               Chef::Log.info("#{@new_resource} reloaded")
             end
           end
         end
   
         def enable_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"
         end
   
         def disable_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"
         end
   
         def start_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :start"
         end
   
         def stop_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :stop"
         end 
         
         def restart_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :restart"
         end
   
         def reload_service
           raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :restart"
         end
    
       end
     end
   end

lib/chef/solr_query/lucene_nodes.rb

   #
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'treetop'
   
   module Lucene
     SEP = "__=__"
   
     class Term < Treetop::Runtime::SyntaxNode
       def to_array
         "T:#{self.text_value}"
       end
   
       def transform
         self.text_value
       end
     end
   
     class Field < Treetop::Runtime::SyntaxNode
       def to_array
         field = self.elements[0].text_value
         term = self.elements[1].to_array
         "(F:#{field} #{term})"
       end
   
       def transform
         field = self.elements[0].text_value
         term = self.elements[1]
         if term.is_a? Phrase
           str = term.transform
           # remove quotes
           str = str[1 ... (str.length - 1)]
           "content:\"#{field}#{SEP}#{str}\""
         else
           "content:#{field}#{SEP}#{term.transform}"
         end
       end
     end
   
     class FieldRange < Treetop::Runtime::SyntaxNode
   
       def to_array
         field = self.elements[0].text_value
         range_start = self.elements[1].to_array
         range_end = self.elements[2].to_array
         "(FR:#{field} #{left}#{range_start}#{right} #{left}#{range_end}#{right})"
       end
   
       def transform
         field = self.elements[0].text_value
         range_start = self.elements[1].transform
         range_end = self.elements[2].transform
         # FIXME: handle special cases for missing start/end
         if ("*" == range_start && "*" == range_end)
           "content:#{field}#{SEP}*"
         elsif "*" == range_end
           "content:#{left}#{field}#{SEP}#{range_start} TO #{field}#{SEP}\\ufff0#{right}"
         elsif "*" == range_start
           "content:#{left}#{field}#{SEP} TO #{field}#{SEP}#{range_end}#{right}"
         else
           "content:#{left}#{field}#{SEP}#{range_start} TO #{field}#{SEP}#{range_end}#{right}"
         end
       end
   
     end
   
     class InclFieldRange < FieldRange
       def left
         "["
       end
       def right
         "]"
       end
     end
   
     class ExclFieldRange < FieldRange
       def left
         "{"
       end
       def right
         "}"
       end
     end
   
     class RangeValue < Treetop::Runtime::SyntaxNode
       def to_array
         self.text_value
       end
   
       def transform
         to_array
       end
     end
   
     class FieldName < Treetop::Runtime::SyntaxNode
       def to_array
         self.text_value
       end
   
       def transform
         to_array
       end
     end
   
   
     class Body < Treetop::Runtime::SyntaxNode
       def to_array
         self.elements.map { |x| x.to_array }.join(" ")
       end
   
       def transform
         self.elements.map { |x| x.transform }.join(" ")
       end
     end
   
     class Group < Treetop::Runtime::SyntaxNode
       def to_array
         "(" + self.elements[0].to_array + ")"
       end
   
       def transform
         "(" + self.elements[0].transform + ")"
       end
     end
   
     class BinaryOp < Treetop::Runtime::SyntaxNode
       def to_array
         op = self.elements[1].to_array
         a = self.elements[0].to_array
         b = self.elements[2].to_array
         "(#{op} #{a} #{b})"
       end
   
       def transform
         op = self.elements[1].transform
         a = self.elements[0].transform
         b = self.elements[2].transform
         "#{a} #{op} #{b}"
       end
     end
   
     class AndOperator < Treetop::Runtime::SyntaxNode
       def to_array
         "OP:AND"
       end
   
       def transform
         "AND"
       end
     end
   
       class OrOperator < Treetop::Runtime::SyntaxNode
       def to_array
         "OP:OR"
       end
   
       def transform
         "OR"
       end
     end
   
     class FuzzyOp < Treetop::Runtime::SyntaxNode
       def to_array
         a = self.elements[0].to_array
         param = self.elements[1]
         if param
           "(OP:~ #{a} #{param.to_array})"
         else
           "(OP:~ #{a})"
         end
       end
   
       def transform
         a = self.elements[0].transform
         param = self.elements[1]
         if param
           "#{a}~#{param.transform}"
         else
           "#{a}~"
         end
       end
     end
   
     class BoostOp < Treetop::Runtime::SyntaxNode
       def to_array
         a = self.elements[0].to_array
         param = self.elements[1]
         "(OP:^ #{a} #{param.to_array})"
       end
   
       def transform
         a = self.elements[0].transform
         param = self.elements[1]
         "#{a}^#{param.transform}"
       end
     end
   
     class FuzzyParam < Treetop::Runtime::SyntaxNode
       def to_array
         self.text_value
       end
   
       def transform
         self.text_value
       end
     end
   
     class UnaryOp < Treetop::Runtime::SyntaxNode
       def to_array
         op = self.elements[0].to_array
         a = self.elements[1].to_array
         "(#{op} #{a})"
       end
   
       def transform
         op = self.elements[0].transform
         a = self.elements[1].transform
         spc = case op
               when "+", "-"
                 ""
               else
                 " "
               end
         "#{op}#{spc}#{a}"
       end
   
     end
     
     class NotOperator < Treetop::Runtime::SyntaxNode
       def to_array
         "OP:NOT"
       end
   
       def transform
         "NOT"
       end
   
     end
   
     class RequiredOperator < Treetop::Runtime::SyntaxNode
       def to_array
         "OP:+"
       end
   
       def transform
         "+"
       end
   
     end
   
     class ProhibitedOperator < Treetop::Runtime::SyntaxNode
       def to_array
         "OP:-"
       end
   
       def transform
         "-"
       end
     end
   
     class Phrase < Treetop::Runtime::SyntaxNode
       def to_array
         "STR:#{self.text_value}"
       end
   
       def transform
         "#{self.text_value}"
       end
     end
   end

lib/chef/provider/subversion.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   require 'chef/log'
   require 'chef/provider'
   require 'chef/mixin/command'
   require 'fileutils'
   
   class Chef
     class Provider
       class Subversion < Chef::Provider
   
         include Chef::Mixin::Command
   
         def load_current_resource
           @current_resource = Chef::Resource::Subversion.new(@new_resource.name)
   
           unless [:export, :force_export].include?(Array(@new_resource.action).first)
             if current_revision = find_current_revision
               @current_resource.revision current_revision
             end
           end
         end
   
         def action_checkout
           assert_target_directory_valid!
           if target_dir_non_existant_or_empty?
             run_command(run_options(:command => checkout_command))
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do"
           end
         end
   
         def action_export
           assert_target_directory_valid!
           if target_dir_non_existant_or_empty?
             run_command(run_options(:command => export_command))
             @new_resource.updated_by_last_action(true)
           else
             Chef::Log.debug "#{@new_resource} export destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do"
           end
         end
   
         def action_force_export
           assert_target_directory_valid!
           run_command(run_options(:command => export_command))
           @new_resource.updated_by_last_action(true)
         end
   
         def action_sync
           assert_target_directory_valid!
           if ::File.exist?(::File.join(@new_resource.destination, ".svn"))
             current_rev = find_current_revision
             Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{revision_int}"
             unless current_revision_matches_target_revision?
               run_command(run_options(:command => sync_command))
               Chef::Log.info "#{@new_resource} updated to revision: #{revision_int}"
               @new_resource.updated_by_last_action(true)
             end
           else
             action_checkout
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def sync_command
           c = scm :update, @new_resource.svn_arguments, verbose, authentication, "-r#{revision_int}", @new_resource.destination
           Chef::Log.debug "#{@new_resource} updated working copy #{@new_resource.destination} to revision #{@new_resource.revision}"
   				c
         end
   
         def checkout_command
           c = scm :checkout, @new_resource.svn_arguments, verbose, authentication,
               "-r#{revision_int}", @new_resource.repository, @new_resource.destination
           Chef::Log.info "#{@new_resource} checked out #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}"
   				c
         end
   
         def export_command
           args = ["--force"]
           args << @new_resource.svn_arguments << verbose << authentication <<
               "-r#{revision_int}" << @new_resource.repository << @new_resource.destination
           c = scm :export, *args
           Chef::Log.info "#{@new_resource} exported #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}"
   				c
         end
   
         # If the specified revision isn't an integer ("HEAD" for example), look
         # up the revision id by asking the server
         # If the specified revision is an integer, trust it.
         def revision_int
           @revision_int ||= begin
             if @new_resource.revision =~ /^\d+$/
               @new_resource.revision
             else
               command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
               status, svn_info, error_message = output_of_command(command, run_options)
               handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
               extract_revision_info(svn_info)
             end
           end
         end
   
         alias :revision_slug :revision_int
   
         def find_current_revision
           return nil unless ::File.exist?(::File.join(@new_resource.destination, ".svn"))
           command = scm(:info)
           status, svn_info, error_message = output_of_command(command, run_options(:cwd => cwd))
   
           unless [0,1].include?(status.exitstatus)
             handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
           end
           extract_revision_info(svn_info)
         end
   
         def current_revision_matches_target_revision?
           (!@current_resource.revision.nil?) && (revision_int.strip.to_i == @current_resource.revision.strip.to_i)
         end
   
         def run_options(run_opts={})
           run_opts[:user] = @new_resource.user if @new_resource.user
           run_opts[:group] = @new_resource.group if @new_resource.group
           run_opts
         end
   
         private
   
         def cwd
           @new_resource.destination
         end
   
         def verbose
           "-q"
         end
   
         def extract_revision_info(svn_info)
           begin
             repo_attrs = YAML.load(svn_info)
           rescue ArgumentError
             # YAML doesn't appreciate input like "svn: '/tmp/deploydir' is not a working copy\n"
             return nil
           end
           raise "Could not parse `svn info` data: #{svn_info}" unless repo_attrs.kind_of?(Hash)
           rev = (repo_attrs['Last Changed Rev'] || repo_attrs['Revision']).to_s
           Chef::Log.debug "#{@new_resource} resolved revision #{@new_resource.revision} to #{rev}"
           rev
         end
   
         # If a username is configured for the SCM, return the command-line
         # switches for that. Note that we don't need to return the password
         # switch, since Capistrano will check for that prompt in the output
         # and will respond appropriately.
         def authentication
           return "" unless @new_resource.svn_username
           result = "--username #{@new_resource.svn_username} "
           result << "--password #{@new_resource.svn_password} "
           result
         end
   
         def scm(*args)
           ['svn', *args].compact.join(" ")
         end
         
         # TODO these methods are the same as the git provider...need to REFACTOR
         # ...the subversion and git providers should extend from the same parent
         def assert_target_directory_valid!
           target_parent_directory = ::File.dirname(@new_resource.destination)
           unless ::File.directory?(target_parent_directory)
             msg = "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist"
             raise Chef::Exceptions::MissingParentDirectory, msg
           end
         end
   
         def target_dir_non_existant_or_empty?
           !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
         end
       end
     end
   end

lib/chef/cookbook/remote_file_vendor.rb

   #
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/cookbook/file_vendor'
   
   class Chef
     class Cookbook
       # == Chef::Cookbook::RemoteFileVendor
       # This FileVendor loads files by either fetching them from the local cache, or
       # if not available, loading them from the remote server.
       class RemoteFileVendor < FileVendor
         
         def initialize(manifest, rest)
           @manifest = manifest
           @cookbook_name = @manifest[:cookbook_name]
           @rest = rest
         end
         
         # Implements abstract base's requirement. It looks in the
         # Chef::Config.cookbook_path file hierarchy for the requested
         # file.
         def get_filename(filename)
           if filename =~ /([^\/]+)\/(.+)$/
             segment = $1
           else
             raise "get_filename: Cannot determine segment/filename for incoming filename #{filename}"
           end
           
           raise "No such segment #{segment} in cookbook #{@cookbook_name}" unless @manifest[segment]
           found_manifest_record = @manifest[segment].find {|manifest_record| manifest_record[:path] == filename }
           raise "No such file #{filename} in #{@cookbook_name}" unless found_manifest_record
           
           cache_filename = File.join("cookbooks", @cookbook_name, found_manifest_record['path'])
           
           # update valid_cache_entries so the upstream cache cleaner knows what
           # we've used.
           validate_cached_copy(cache_filename)
   
           current_checksum = nil
           if Chef::FileCache.has_key?(cache_filename)
             current_checksum = Chef::CookbookVersion.checksum_cookbook_file(Chef::FileCache.load(cache_filename, false))
           end
   
           # If the checksums are different between on-disk (current) and on-server
           # (remote, per manifest), do the update. This will also execute if there
           # is no current checksum.
           if current_checksum != found_manifest_record['checksum']
             raw_file = @rest.get_rest(found_manifest_record[:url], true)
   
             Chef::Log.debug("Storing updated #{cache_filename} in the cache.")
             Chef::FileCache.move_to(raw_file.path, cache_filename)
           else
             Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
           end
   
           full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
   
           # return the filename, not the contents (second argument= false)
           full_path_cache_filename
         end
   
         def validate_cached_copy(cache_filename)
           valid_cache_entries[cache_filename] = true
         end
   
         def valid_cache_entries
           Chef::CookbookVersion.valid_cache_entries
         end
         
       end
     end
   end

lib/chef/provider.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/from_file'
   require 'chef/mixin/convert_to_class_name'
   require 'chef/mixin/recipe_definition_dsl_core'
   
   class Chef
     class Provider
       
       include Chef::Mixin::RecipeDefinitionDSLCore
       
       attr_accessor :new_resource, :current_resource, :run_context
       
       def initialize(new_resource, run_context)
         @new_resource = new_resource
         @current_resource = nil
         @run_context = run_context
       end
   
       def node
         run_context && run_context.node
       end
   
       # Used by providers supporting embedded recipes
       def resource_collection
         run_context && run_context.resource_collection
       end
   
       def cookbook_name
         new_resource.cookbook_name
       end
       
       def load_current_resource
         raise Chef::Exceptions::Override, "You must override load_current_resource in #{self.to_s}"
       end
       
       def action_nothing
         Chef::Log.debug("Doing nothing for #{@new_resource.to_s}")
         true
       end
       
       protected
       
       def recipe_eval(&block)
         # This block has new resource definitions within it, which
         # essentially makes it an in-line Chef run. Save our current
         # run_context and create one anew, so the new Chef run only
         # executes the embedded resources.
         #
         # TODO: timh,cw: 2010-5-14: This means that the resources within
         # this block cannot interact with resources outside, e.g.,
         # manipulating notifies.
         saved_run_context = @run_context
         @run_context = @run_context.dup
         @run_context.resource_collection = Chef::ResourceCollection.new
         instance_eval(&block)
         Chef::Runner.new(@run_context).converge
         
         @run_context = saved_run_context
       end
       
       public
       
       class << self
         include Chef::Mixin::ConvertToClassName
         
         def build_from_file(cookbook_name, filename, run_context)
           pname = filename_to_qualified_string(cookbook_name, filename)
           
           # Add log entry if we override an existing light-weight provider.
           class_name = convert_to_class_name(pname)
           overriding = Chef::Provider.const_defined?(class_name)
           Chef::Log.info("#{class_name} light-weight provider already initialized -- overriding!") if overriding
           
           new_provider_class = Class.new self do |cls|
             
             def load_current_resource
               # silence Chef::Exceptions::Override exception
             end
             
             class << cls
               include Chef::Mixin::FromFile
               
               # setup DSL's shortcut methods
               def action(name, &block)
                 define_method("action_#{name.to_s}") do
                   instance_eval(&block)
                 end
               end
             end
             
             # load provider definition from file
             cls.class_from_file(filename)
           end
           
           # register new class as a Chef::Provider
           pname = filename_to_qualified_string(cookbook_name, filename)
           class_name = convert_to_class_name(pname)
           Chef::Provider.const_set(class_name, new_provider_class)
           Chef::Log.debug("Loaded contents of #{filename} into a provider named #{pname} defined in Chef::Provider::#{class_name}")
           
           new_provider_class
         end
       end
   
     end
   end

lib/chef/knife/cookbook_download.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookDownload < Knife
   
         attr_reader :version
         attr_accessor :cookbook_name
   
         deps do
           require 'chef/cookbook_version'
         end
   
         banner "knife cookbook download COOKBOOK [VERSION] (options)"
   
         option :latest,
          :short => "-N",
          :long => "--latest",
          :description => "The version of the cookbook to download",
          :boolean => true
   
         option :download_directory,
          :short => "-d DOWNLOAD_DIRECTORY",
          :long => "--dir DOWNLOAD_DIRECTORY",
          :description => "The directory to download the cookbook into",
          :default => Dir.pwd
   
         option :force,
          :short => "-f",
          :long => "--force",
          :description => "Force download over the download directory if it exists"
   
         # TODO: tim/cw: 5-23-2010: need to implement knife-side
         # specificity for downloads - need to implement --platform and
         # --fqdn here
         def run
           @cookbook_name, @version = @name_args
   
           if @cookbook_name.nil?
             show_usage
             ui.fatal("You must specify a cookbook name")
             exit 1
           elsif @version.nil?
             @version = determine_version
           end
   
           ui.info("Downloading #{@cookbook_name} cookbook version #{@version}")
   
           cookbook = rest.get_rest("cookbooks/#{@cookbook_name}/#{@version}")
           manifest = cookbook.manifest
   
           basedir = File.join(config[:download_directory], "#{@cookbook_name}-#{cookbook.version}")
           if File.exists?(basedir)
             if config[:force]
               Chef::Log.debug("Deleting #{basedir}")
               FileUtils.rm_rf(basedir)
             else
               ui.fatal("Directory #{basedir} exists, use --force to overwrite")
               exit
             end
           end
   
           Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
             next unless manifest.has_key?(segment)
             ui.info("Downloading #{segment}")
             manifest[segment].each do |segment_file|
               dest = File.join(basedir, segment_file['path'].gsub('/', File::SEPARATOR))
               Chef::Log.debug("Downloading #{segment_file['path']} to #{dest}")
               FileUtils.mkdir_p(File.dirname(dest))
               rest.sign_on_redirect = false
               tempfile = rest.get_rest(segment_file['url'], true)
               FileUtils.mv(tempfile.path, dest)
             end
           end
           ui.info("Cookbook downloaded to #{basedir}")
         end
   
         def determine_version
           if available_versions.size == 1
             @version = available_versions.first
           elsif config[:latest]
             @version = available_versions.map { |v| Chef::Version.new(v) }.sort.last
           else
             ask_which_version
           end
         end
   
         def available_versions
           @available_versions ||= begin
             versions = Chef::CookbookVersion.available_versions(@cookbook_name).map do |version|
               Chef::Version.new(version)
             end
             versions.sort!
             versions
           end
           @available_versions
         end
   
         def ask_which_version
           question = "Which version do you want to download?\n"
           valid_responses = {}
           available_versions.each_with_index do |version, index|
             valid_responses[(index + 1).to_s] = version
             question << "#{index + 1}. #{@cookbook_name} #{version}\n"
           end
           question += "\n"
           response = ask_question(question).strip
   
           unless @version = valid_responses[response]
             ui.error("'#{response}' is not a valid value.")
             exit(1)
           end
           @version
         end
   
       end
     end
   end

lib/chef/runner.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/params_validate'
   require 'chef/node'
   require 'chef/resource_collection'
   require 'chef/platform'
   
   class Chef
     # == Chef::Runner
     # This class is responsible for executing the steps in a Chef run.
     class Runner
   
       attr_reader :run_context
   
       attr_reader :delayed_actions
   
       include Chef::Mixin::ParamsValidate
   
       def initialize(run_context)
         @run_context      = run_context
         @delayed_actions  = []
       end
   
       # Determine the appropriate provider for the given resource, then
       # execute it.
       def run_action(resource, action)
         resource.run_action(action)
   
         # Execute any immediate and queue up any delayed notifications
         # associated with the resource, but only if it was updated *this time*
         # we ran an action on it.
         if resource.updated_by_last_action?
           resource.immediate_notifications.each do |notification|
             Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)")
             run_action(notification.resource, notification.action)
           end
   
           resource.delayed_notifications.each do |notification|
             if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
               Chef::Log.info( "#{resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
                               " (delayed), as it's already been queued")
             else
               delayed_actions << notification
             end
           end
         end
       end
   
       # Iterates over the +resource_collection+ in the +run_context+ calling
       # +run_action+ for each resource in turn.
       def converge
         # Resolve all lazy/forward references in notifications
         run_context.resource_collection.each do |resource|
           resource.resolve_notification_references
         end
   
         # Execute each resource.
         run_context.resource_collection.execute_each_resource do |resource|
           begin
             Chef::Log.debug("Processing #{resource} on #{run_context.node.name}")
   
             # Execute each of this resource's actions.
             Array(resource.action).each {|action| run_action(resource, action)}
           rescue => e
             Chef::Log.error("#{resource} (#{resource.source_line}) had an error:\n#{e}\n#{e.backtrace.join("\n")}")
             if resource.retries > 0
               resource.retries -= 1
               Chef::Log.info("Retrying execution of #{resource}, #{resource.retries} attempt(s) left")
               sleep resource.retry_delay
               retry
             end
             raise e unless resource.ignore_failure
           end
         end
   
         # Run all our :delayed actions
         delayed_actions.each do |notification|
           Chef::Log.info( "#{notification.notifying_resource} sending #{notification.action}"\
                           " action to #{notification.resource} (delayed)")
           # Struct of resource/action to call
           run_action(notification.resource, notification.action)
         end
   
         true
       end
     end
   end

lib/chef/solr_query.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/xml_escape'
   require 'chef/log'
   require 'chef/config'
   require 'chef/couchdb'
   require 'chef/solr_query/solr_http_request'
   require 'chef/solr_query/query_transform'
   
   class Chef
     class SolrQuery
   
       ID_KEY = "X_CHEF_id_CHEF_X"
       DEFAULT_PARAMS = Mash.new(:start => 0, :rows => 1000, :sort => "#{ID_KEY} asc", :wt => 'json', :indent => 'off').freeze
       FILTER_PARAM_MAP = {:database => 'X_CHEF_database_CHEF_X', :type => "X_CHEF_type_CHEF_X", :data_bag  => 'data_bag'}
       VALID_PARAMS = [:start,:rows,:sort,:q,:type]
       BUILTIN_SEARCH_TYPES = ["role","node","client","environment"]
       DATA_BAG_ITEM = 'data_bag_item'
   
       include Chef::Mixin::XMLEscape
   
       attr_accessor :query
       attr_accessor :params
   
       # Create a new Query object - takes the solr_url and optional
       # Chef::CouchDB object to inflate objects into.
       def initialize(couchdb = nil)
         @filter_query = {}
         @params = {}
   
         if couchdb.nil?
           @database = Chef::Config[:couchdb_database]
           @couchdb = Chef::CouchDB.new(nil, Chef::Config[:couchdb_database])
         else
           unless couchdb.respond_to?(:couchdb_database)
             Chef::Log.warn("Passing the database name to Chef::Solr::Query initialization is deprecated. Please pass in the Chef::CouchDB object instead.")
             @database = couchdb
             @couchdb = Chef::CouchDB.new(nil, couchdb)
           else
             @database = couchdb.couchdb_database
             @couchdb = couchdb
           end
         end
       end
   
       def self.from_params(params, couchdb=nil)
         query = new(couchdb)
         query.params = VALID_PARAMS.inject({}) do |p, param_name|
           p[param_name] = params[param_name] if params.key?(param_name)
           p
         end
         query.update_filter_query_from_params
         query.update_query_from_params
         query
       end
   
       def filter_by(filter_query_params)
         filter_query_params.each do |key, value|
           @filter_query[FILTER_PARAM_MAP[key]] = value
         end
       end
   
       def filter_query
         @filter_query.map { |param, value| "+#{param}:#{value}" }.join(' ')
       end
   
       def filter_by_type(type)
         case type
         when *BUILTIN_SEARCH_TYPES
           filter_by(:type => type)
         else
           filter_by(:type => DATA_BAG_ITEM, :data_bag => type)
         end
       end
   
       def update_filter_query_from_params
         filter_by(:database => @database)
         filter_by_type(params.delete(:type))
       end
   
       def update_query_from_params
         original_query = URI.decode(params.delete(:q) || "*:*")
         @query = Chef::SolrQuery::QueryTransform.transform(original_query)
       end
   
       def objects
         if !object_ids.empty?
           @bulk_objects ||= @couchdb.bulk_get(object_ids)
           Chef::Log.debug { "Bulk get of objects: #{@bulk_objects.inspect}" }
           @bulk_objects
         else
           []
         end
       end
   
       def object_ids
         @object_ids ||= results["response"]["docs"].map { |d| d[ID_KEY] }
       end
   
       def results
         @results ||= SolrHTTPRequest.select(self.to_hash)
       end
   
       # Search Solr for objects of a given type, for a given query.
       def search
         { "rows" => objects, "start" => results["response"]["start"],
           "total" => results["response"]["numFound"] }
       end
   
       def to_hash
         options = DEFAULT_PARAMS.merge(params)
         options[:fq] = filter_query
         options[:q] = @query
         options
       end
   
       START_XML = "\n".freeze
       START_DELETE_BY_QUERY = "".freeze
       END_DELETE_BY_QUERY = "\n".freeze
       COMMIT = "\n".freeze
   
       def commit(opts={})
         SolrHTTPRequest.update("#{START_XML}#{COMMIT}")
       end
   
       def delete_database(db)
         query_data = xml_escape("X_CHEF_database_CHEF_X:#{db}")
         xml = "#{START_XML}#{START_DELETE_BY_QUERY}#{query_data}#{END_DELETE_BY_QUERY}"
         SolrHTTPRequest.update(xml)
         commit
       end
   
       def rebuild_index(db=Chef::Config[:couchdb_database])
         delete_database(db)
   
         results = {}
         [Chef::ApiClient, Chef::Node, Chef::Role, Chef::Environment].each do |klass|
           results[klass.name] = reindex_all(klass) ? "success" : "failed"
         end
         databags = Chef::DataBag.cdb_list(true)
         Chef::Log.info("Reloading #{databags.size.to_s} #{Chef::DataBag} objects into the indexer")
         databags.each { |i| i.add_to_index; i.list(true).each { |x| x.add_to_index } }
         results[Chef::DataBag.name] = "success"
         results
       end
   
       def reindex_all(klass, metadata={})
         begin
           items = klass.cdb_list(true)
           Chef::Log.info("Reloading #{items.size.to_s} #{klass.name} objects into the indexer")
           items.each { |i| i.add_to_index }
         rescue Net::HTTPServerException => e
           # 404s are okay, there might not be any of that kind of object...
           if e.message =~ /Not Found/
             Chef::Log.warn("Could not load #{klass.name} objects from couch for re-indexing (this is ok if you don't have any of these)")
             return false
           else
             raise e
           end
         rescue Exception => e
           Chef::Log.fatal("Chef encountered an error while attempting to load #{klass.name} objects back into the index")
           raise e
         end
         true
       end
   
   
     end
   end

lib/chef/resource/mdadm.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Mdadm < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :mdadm
   
           @chunk = 16
           @devices = []
           @exists = false
           @level = 1
           @raid_device = name
   
           @action = :create
           @allowed_actions.push(:create, :assemble, :stop)
         end
   
         def chunk(arg=nil)
           set_or_return(
             :chunk,
             arg,
             :kind_of => [ Integer ]
           )
         end
   
         def devices(arg=nil)
           set_or_return(
             :devices,
             arg,
             :kind_of => [ Array ]
           )
         end
   
         def exists(arg=nil)
           set_or_return(
             :exists,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def level(arg=nil)
           set_or_return(
             :level,
             arg,
             :kind_of => [ Integer ]
           )
         end
   
         def raid_device(arg=nil)
           set_or_return(
             :raid_device,
             arg,
             :kind_of => [ String ]
           )
         end
   
   
       end
     end
   end

lib/chef/knife/cookbook_metadata.rb

   #
   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookMetadata < Knife
   
         deps do
           require 'chef/cookbook_loader'
           require 'chef/cookbook/metadata'
         end
   
         banner "knife cookbook metadata COOKBOOK (options)"
   
         option :cookbook_path,
           :short => "-o PATH:PATH",
           :long => "--cookbook-path PATH:PATH",
           :description => "A colon-separated path to look for cookbooks in",
           :proc => lambda { |o| o.split(":") }
   
         option :all,
           :short => "-a",
           :long => "--all",
           :description => "Generate metadata for all cookbooks, rather than just a single cookbook"
   
         def run
           config[:cookbook_path] ||= Chef::Config[:cookbook_path]
   
           if config[:all]
             cl = Chef::CookbookLoader.new(config[:cookbook_path])
             cl.each do |cname, cookbook|
               generate_metadata(cname.to_s)
             end
           else
             cookbook_name = @name_args[0]
             if cookbook_name.nil? || cookbook_name.empty?
               ui.error "You must specify the cookbook to generate metadata for, or use the --all option."
               exit 1
             end
             generate_metadata(cookbook_name)
           end
         end
   
         def generate_metadata(cookbook)
           Array(config[:cookbook_path]).reverse.each do |path|
             file = File.expand_path(File.join(path, cookbook, 'metadata.rb'))
             if File.exists?(file)
               generate_metadata_from_file(cookbook, file)
             else
               validate_metadata_json(path, cookbook)
             end
           end
         end
   
         def generate_metadata_from_file(cookbook, file)
           ui.info("Generating metadata for #{cookbook} from #{file}")
           md = Chef::Cookbook::Metadata.new
           md.name(cookbook)
           md.from_file(file)
           json_file = File.join(File.dirname(file), 'metadata.json')
           File.open(json_file, "w") do |f|
             f.write(Chef::JSONCompat.to_json_pretty(md))
           end
           generated = true
           Chef::Log.debug("Generated #{json_file}")
         rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e
           ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax."
           ui.stderr.puts "in #{file}:"
           ui.stderr.puts
           ui.stderr.puts e.message
           exit 1
         end
   
         def validate_metadata_json(path, cookbook)
           json_file = File.join(path, cookbook, 'metadata.json')
           if File.exist?(json_file)
             Chef::Cookbook::Metadata.validate_json(IO.read(json_file))
           end
         rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e
           ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax."
           ui.stderr.puts "in #{json_file}:"
           ui.stderr.puts
           ui.stderr.puts e.message
           exit 1
         end
   
       end
     end
   end

lib/chef/resource/package.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Package < Chef::Resource
   
         def initialize(name, run_context=nil)
           super
           @resource_name = :package
           @package_name = name
           @version = nil
           @candidate_version = nil
           @response_file = nil
           @source = nil
           @action = :install
           @options = nil
           @allowed_actions.push(:install, :upgrade, :remove, :purge, :reconfig)
         end
   
         def package_name(arg=nil)
           set_or_return(
             :package_name,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def version(arg=nil)
           set_or_return(
             :version,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def response_file(arg=nil)
           set_or_return(
             :response_file,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def source(arg=nil)
           set_or_return(
             :source,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def options(arg=nil)
           set_or_return(
   	  :options,
   	  arg,
   	  :kind_of => [ String ]
   	)
         end
   
       end
     end
   end

lib/chef/knife/node_bulk_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeBulkDelete < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node bulk delete REGEX (options)"
   
         def run
           if name_args.length < 1
             ui.fatal("You must supply a regular expression to match the results against")
             exit 42
           end
   
   
           nodes_to_delete = {}
           matcher = /#{name_args[0]}/
   
           all_nodes.each do |name, node|
             next unless name =~ matcher
             nodes_to_delete[name] = node
           end
   
           if nodes_to_delete.empty?
             ui.msg "No nodes match the expression /#{name_args[0]}/"
             exit 0
           end
   
           ui.msg("The following nodes will be deleted:")
           ui.msg("")
           ui.msg(ui.list(nodes_to_delete.keys.sort, :columns_down))
           ui.msg("")
           ui.confirm("Are you sure you want to delete these nodes")
   
   
           nodes_to_delete.sort.each do |name, node|
             node.destroy
             ui.msg("Deleted node #{name}")
           end
         end
   
         def all_nodes
           node_uris_by_name = Chef::Node.list
   
           node_uris_by_name.keys.inject({}) do |nodes_by_name, name|
             nodes_by_name[name] = Chef::Node.new.tap {|n| n.name(name)}
             nodes_by_name
           end
         end
   
       end
     end
   end
   
   
   
   

lib/chef/knife/cookbook_bulk_delete.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookBulkDelete < Knife
   
         deps do
           require 'chef/knife/cookbook_delete'
           require 'chef/cookbook_version'
         end
   
         option :purge, :short => '-p', :long => '--purge', :boolean => true, :description => 'Permanently remove files from backing data store'
   
         banner "knife cookbook bulk delete REGEX (options)"
   
         def run
           unless regex_str = @name_args.first
             ui.fatal("You must supply a regular expression to match the results against")
             exit 42
           end
   
           regex = Regexp.new(regex_str)
   
           all_cookbooks = Chef::CookbookVersion.list
           cookbooks_names = all_cookbooks.keys.grep(regex)
           cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash }
           ui.msg "All versions of the following cookbooks will be deleted:"
           ui.msg ""
           ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down)
           ui.msg ""
   
           unless config[:yes]
             ui.confirm("Do you really want to delete these cookbooks? (Y/N) ", false)
   
             if config[:purge]
               ui.msg("Files that are common to multiple cookbooks are shared, so purging the files may break other cookbooks.")
               ui.confirm("Are you sure you want to purge files instead of just deleting the cookbooks")
             end
             ui.msg ""
           end
   
   
           cookbooks_names.each do |cookbook_name|
             versions = rest.get_rest("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map {|v| v["version"]}.flatten
             versions.each do |version|
               object = rest.delete_rest("cookbooks/#{cookbook_name}/#{version}#{config[:purge] ? "?purge=true" : ""}")
               ui.info("Deleted cookbook  #{cookbook_name.ljust(25)} [#{version}]")
             end
           end
         end
       end
     end
   end

lib/chef/provider/mount/windows.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/mount'
   if RUBY_PLATFORM =~ /mswin|mingw32|windows/
     require 'chef/util/windows/net_use'
     require 'chef/util/windows/volume'
   end
   
   class Chef
     class Provider
       class Mount
         class Windows < Chef::Provider::Mount
   
           def is_volume(name)
             name =~ /^\\\\\?\\Volume\{[\w-]+\}\\$/ ? true : false
           end
   
           def initialize(new_resource, run_context)
             super
             @mount = nil
           end
   
           def load_current_resource
             if is_volume(@new_resource.device)
               @mount = Chef::Util::Windows::Volume.new(@new_resource.name)
             else #assume network drive
               @mount = Chef::Util::Windows::NetUse.new(@new_resource.name)
             end
   
             @current_resource = Chef::Resource::Mount.new(@new_resource.name)
             @current_resource.mount_point(@new_resource.mount_point)
             Chef::Log.debug("Checking for mount point #{@current_resource.mount_point}")
   
             begin
               @current_resource.device(@mount.device)
               Chef::Log.debug("#{@current_resource.device} mounted on #{@new_resource.mount_point}")
               @current_resource.mounted(true)
             rescue ArgumentError => e
               @current_resource.mounted(false)
               Chef::Log.debug("#{@new_resource.mount_point} is not mounted: #{e.message}")
             end
           end
   
           def mount_fs
             unless @current_resource.mounted
               @mount.add(@new_resource.device)
               Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
             else
               Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}")
             end
           end
   
           def umount_fs
             if @current_resource.mounted
               @mount.delete
               Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}")
             else
               Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}")
             end
           end
   
         end
       end
     end
   end

lib/chef/resource/group.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Resource
       class Group < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :group
           @group_name = name
           @gid = nil
           @members = []
           @action = :create
           @append = false
           @allowed_actions.push(:create, :remove, :modify, :manage)
         end
         
         def group_name(arg=nil)
           set_or_return(
             :group_name,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def gid(arg=nil)
           set_or_return(
             :gid,
             arg,
             :kind_of => [ Integer ]
           )
         end
   
         def members(arg=nil)
           converted_members = arg.is_a?(String) ? [].push(arg) : arg
           set_or_return(
             :members,
             converted_members,
             :kind_of => [ Array ]
           )
         end
   
         alias_method :users, :members
    
         def append(arg=nil)
           set_or_return(
             :append,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def system(arg=nil)
           set_or_return(
             :system,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
       end
     end
   end

lib/chef/knife/core/ui.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009, 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'forwardable'
   require 'chef/knife/core/generic_presenter'
   
   class Chef
     class Knife
   
       #==Chef::Knife::UI
       # The User Interaction class used by knife.
       class UI
   
         extend Forwardable
   
         attr_reader :stdout
         attr_reader :stderr
         attr_reader :stdin
         attr_reader :config
   
         def_delegator :@presenter, :format_list_for_display
         def_delegator :@presenter, :format_for_display
         def_delegator :@presenter, :format_cookbook_list_for_display
   
         def initialize(stdout, stderr, stdin, config)
           @stdout, @stderr, @stdin, @config = stdout, stderr, stdin, config
           @presenter = Chef::Knife::Core::GenericPresenter.new(self, config)
         end
   
         # Creates a new +presenter_class+ object and uses it to format structured
         # data for display. By default, a Chef::Knife::Core::GenericPresenter
         # object is used.
         def use_presenter(presenter_class)
           @presenter = presenter_class.new(self, config)
         end
   
         def highline
           @highline ||= begin
             require 'highline'
             HighLine.new
           end
         end
   
         # Prints a message to stdout. Aliased as +info+ for compatibility with
         # the logger API.
         def msg(message)
           stdout.puts message
         end
   
         alias :info :msg
   
         # Prints a msg to stderr. Used for warn, error, and fatal.
         def err(message)
           stderr.puts message
         end
   
         # Print a warning message
         def warn(message)
           err("#{color('WARNING:', :yellow, :bold)} #{message}")
         end
   
         # Print an error message
         def error(message)
           err("#{color('ERROR:', :red, :bold)} #{message}")
         end
   
         # Print a message describing a fatal error.
         def fatal(message)
           err("#{color('FATAL:', :red, :bold)} #{message}")
         end
   
         def color(string, *colors)
           if color?
             highline.color(string, *colors)
           else
             string
           end
         end
   
         # Should colored output be used? For output to a terminal, this is
         # determined by the value of `config[:color]`. When output is not to a
         # terminal, colored output is never used
         def color?
           Chef::Config[:color] && stdout.tty? && (RUBY_PLATFORM !~ /mswin|mingw32|windows/)
         end
   
         def ask(*args, &block)
           highline.ask(*args, &block)
         end
   
         def list(*args)
           highline.list(*args)
         end
   
         # Formats +data+ using the configured presenter and outputs the result
         # via +msg+. Formatting can be customized by configuring a different
         # presenter. See +use_presenter+
         def output(data)
           msg @presenter.format(data)
         end
   
         # Determines if the output format is a data interchange format, i.e.,
         # JSON or YAML
         def interchange?
           @presenter.interchange?
         end
   
         def ask_question(question, opts={})
           question = question + "[#{opts[:default]}] " if opts[:default]
   
           if opts[:default] and config[:defaults]
             opts[:default]
           else
             stdout.print question
             a = stdin.readline.strip
   
             if opts[:default]
               a.empty? ? opts[:default] : a
             else
               a
             end
           end
         end
   
         def pretty_print(data)
           stdout.puts data
         end
   
         def edit_data(data, parse_output=true)
           output = Chef::JSONCompat.to_json_pretty(data)
   
           if (!config[:no_editor])
             filename = "knife-edit-"
             0.upto(20) { filename += rand(9).to_s }
             filename << ".js"
             filename = File.join(Dir.tmpdir, filename)
             tf = File.open(filename, "w")
             tf.sync = true
             tf.puts output
             tf.close
             raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}")
             tf = File.open(filename, "r")
             output = tf.gets(nil)
             tf.close
             File.unlink(filename)
           end
   
           parse_output ? Chef::JSONCompat.from_json(output) : output
         end
   
         def edit_object(klass, name)
           object = klass.load(name)
   
           output = edit_data(object)
   
           # Only make the save if the user changed the object.
           #
           # Output JSON for the original (object) and edited (output), then parse
           # them without reconstituting the objects into real classes
           # (create_additions=false). Then, compare the resulting simple objects,
           # which will be Array/Hash/String/etc.
           #
           # We wouldn't have to do these shenanigans if all the editable objects
           # implemented to_hash, or if to_json against a hash returned a string
           # with stable key order.
           object_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(object), :create_additions => false)
           output_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(output), :create_additions => false)
           if object_parsed_again != output_parsed_again
             output.save
             self.msg("Saved #{output}")
           else
             self.msg("Object unchanged, not saving")
           end
           output(format_for_display(object)) if config[:print_after]
         end
   
         def confirm(question, append_instructions=true)
           return true if config[:yes]
   
           stdout.print question
           stdout.print "? (Y/N) " if append_instructions
           answer = stdin.readline
           answer.chomp!
           case answer
           when "Y", "y"
             true
           when "N", "n"
             self.msg("You said no, so I'm done here.")
             exit 3
           else
             self.msg("I have no idea what to do with #{answer}")
             self.msg("Just say Y or N, please.")
             confirm(question)
           end
         end
   
       end
     end
   end
   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Link < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :link
           @to = nil
           @action = :create
           @link_type = :symbolic
           @target_file = name
           @allowed_actions.push(:create, :delete)
         end
         
         def to(arg=nil)
           set_or_return(
             :source_file,
             arg,
             :kind_of => String
           )
         end
         
         def target_file(arg=nil)
           set_or_return(
             :target_file,
             arg,
             :kind_of => String
           )
         end
         
         def link_type(arg=nil)
           real_arg = arg.kind_of?(String) ? arg.to_sym : arg
           set_or_return(
             :link_type,
             real_arg,
             :equal_to => [ :symbolic, :hard ]
           )
         end
         
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :regex => Chef::Config[:group_valid_regex]
           )
         end
         
         def owner(arg=nil)
           set_or_return(
             :owner,
             arg,
             :regex => Chef::Config[:user_valid_regex]
           )
         end
   
       end
     end
   end

lib/chef/knife/cookbook_site_install.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
   
       class CookbookSiteInstall < Knife
   
         deps do
           require 'chef/mixin/shell_out'
           require 'chef/knife/core/cookbook_scm_repo'
           require 'chef/cookbook/metadata'
         end
   
         banner "knife cookbook site install COOKBOOK [VERSION] (options)"
         category "cookbook site"
   
         option :no_deps,
          :short => "-D",
          :long => "--no-dependencies",
          :boolean => true,
          :description => "Do not install dependencies automatically"
   
         option :cookbook_path,
           :short => "-o PATH:PATH",
           :long => "--cookbook-path PATH:PATH",
           :description => "A colon-separated path to look for cookbooks in",
           :proc => lambda { |o| o.split(":") }
   
         option :default_branch,
           :short => "-B BRANCH",
           :long => "--branch BRANCH",
           :description => "Default branch to work with",
           :default => "master"
   
         attr_reader :cookbook_name
         attr_reader :vendor_path
   
         def run
           extend Chef::Mixin::ShellOut
   
           if config[:cookbook_path]
             Chef::Config[:cookbook_path] = config[:cookbook_path]
           else
             config[:cookbook_path] = Chef::Config[:cookbook_path]
           end
   
           @cookbook_name = parse_name_args!
           # Check to ensure we have a valid source of cookbooks before continuing
           #
           @install_path = config[:cookbook_path].first
           ui.info "Installing #@cookbook_name to #{@install_path}"
   
           @repo = CookbookSCMRepo.new(@install_path, ui, config)
           #cookbook_path = File.join(vendor_path, name_args[0])
           upstream_file = File.join(@install_path, "#{@cookbook_name}.tar.gz")
   
           @repo.sanity_check
           @repo.reset_to_default_state
           @repo.prepare_to_import(@cookbook_name)
   
           downloader = download_cookbook_to(upstream_file)
           clear_existing_files(File.join(@install_path, @cookbook_name))
           extract_cookbook(upstream_file, @install_path)
   
           # TODO: it'd be better to store these outside the cookbook repo and
           # keep them around, e.g., in ~/Library/Caches on OS X.
           ui.info("removing downloaded tarball")
           shell_out!("rm #{upstream_file}", :cwd => vendor_path)
   
           if @repo.finalize_updates_to(@cookbook_name, downloader.version)
             @repo.reset_to_default_state
             @repo.merge_updates_from(@cookbook_name, downloader.version)
           else
             @repo.reset_to_default_state
           end
   
   
           unless config[:no_deps]
             md = Chef::Cookbook::Metadata.new
             md.from_file(File.join(@install_path, @cookbook_name, "metadata.rb"))
             md.dependencies.each do |cookbook, version_list|
               # Doesn't do versions.. yet
               nv = self.class.new
               nv.config = config
               nv.name_args = [ cookbook ]
               nv.run
             end
           end
         end
   
         def parse_name_args!
           if name_args.empty?
             ui.error("please specify a cookbook to download and install")
             exit 1
           elsif name_args.size > 1
             ui.error("Installing multiple cookbooks at once is not supported")
             exit 1
           else
             name_args.first
           end
         end
   
         def download_cookbook_to(download_path)
           downloader = Chef::Knife::CookbookSiteDownload.new
           downloader.config[:file] = download_path
           downloader.name_args = name_args
           downloader.run
           downloader
         end
   
         def extract_cookbook(upstream_file, version)
           ui.info("Uncompressing #{@cookbook_name} version #{version}.")
           shell_out!("tar zxvf #{upstream_file}", :cwd => @install_path)
         end
   
         def clear_existing_files(cookbook_path)
           ui.info("Removing pre-existing version.")
           shell_out!("rm -r #{cookbook_path}", :cwd => @install_path) if File.directory?(cookbook_path)
         end
   
   
       end
     end
   end
   
   
   
   
   
   

lib/chef/index_queue/amqp_client.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module IndexQueue
       class AmqpClient
         VNODES = 1024
   
         include Singleton
   
         def initialize
           reset!
         end
   
         def reset!
           @amqp_client && amqp_client.connected? && amqp_client.stop
           @amqp_client = nil
           @exchange = nil
         end
         
         def stop
           @amqp_client && @amqp_client.stop
         end
         
         def amqp_client
           unless @amqp_client
             begin
               @amqp_client = Bunny.new(amqp_opts)
               Chef::Log.debug "Starting AMQP connection with client settings: #{@amqp_client.inspect}"
               @amqp_client.start
               @amqp_client.qos(:prefetch_count => 1)
             rescue Bunny::ServerDownError => e
               Chef::Log.fatal "Could not connect to rabbitmq. Is it running, reachable, and configured correctly?"
               raise e
             rescue Bunny::ProtocolError => e
               Chef::Log.fatal "Connection to rabbitmq refused. Check your rabbitmq configuration and chef's amqp* settings"
               raise e
             end
           end
           @amqp_client
         end
   
         def exchange
           @exchange ||= amqp_client.exchange("chef-indexer", :durable => true, :type => :fanout)
         end
         
         def disconnected!
           Chef::Log.error("Disconnected from the AMQP Broker (RabbitMQ)")
           @amqp_client = nil
           reset!
         end
   
         def queue_for_object(obj_id)
           retries = 0
           vnode_tag = obj_id_to_int(obj_id) % VNODES
           begin
             yield amqp_client.queue("vnode-#{vnode_tag}", :passive => false, :durable => true, :exclusive => false, :auto_delete => false)
           rescue Bunny::ServerDownError, Bunny::ConnectionError, Errno::ECONNRESET
             disconnected!
             if (retries += 1) < 2
               Chef::Log.info("Attempting to reconnect to the AMQP broker")
               retry
             else
               Chef::Log.fatal("Could not re-connect to the AMQP broker, giving up")
               raise
             end
           end
         end
   
         private
   
         # Sometimes object ids are "proper" UUIDs, like "64bc00eb-120b-b6a2-ec0e-34fc90d151be"
         # and sometimes they omit the dashes, like "64bc00eb120bb6a2ec0e34fc90d151be"
         # UUIDTools uses different methods to parse the different styles.
         def obj_id_to_int(obj_id)
           UUIDTools::UUID.parse(obj_id).to_i
         rescue ArgumentError
           UUIDTools::UUID.parse_hexdigest(obj_id).to_i
         end
         
         def durable_queue?
             Chef::Config[:amqp_consumer_id]
         end
         
         def consumer_id
           Chef::Config[:amqp_consumer_id] || UUIDTools::UUID.random_create.to_s
         end
   
         def amqp_opts
           { :spec   => '08',
             :host   => Chef::Config[:amqp_host],
             :port   => Chef::Config[:amqp_port],
             :user   => Chef::Config[:amqp_user],
             :pass   => Chef::Config[:amqp_pass],
             :vhost  => Chef::Config[:amqp_vhost]}
         end
   
       end
     end
   end
   

lib/chef/resource/erl_call.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class ErlCall < Chef::Resource
   
         # erl_call : http://erlang.org/doc/man/erl_call.html
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :erl_call
   
           @code = "q()." # your erlang code goes here
           @cookie = nil # cookie of the erlang node
           @distributed = false # if you want to have a distributed erlang node
           @name_type = "sname" # type of erlang hostname name or sname
           @node_name = "chef@localhost" # the erlang node hostname
   
           @action = "run"
           @allowed_actions.push(:run)
         end
   
         def code(arg=nil)
           set_or_return(
             :code,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def cookie(arg=nil)
           set_or_return(
             :cookie,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def distributed(arg=nil)
           set_or_return(
             :distributed,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
         def name_type(arg=nil)
           set_or_return(
             :name_type,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def node_name(arg=nil)
           set_or_return(
             :node_name,
             arg,
             :kind_of => [ String ]
           )
         end
   
       end
     end
   end

lib/chef/openid_registration.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/couchdb'
   require 'chef/index_queue'
   require 'digest/sha1'
   require 'chef/json_compat'
   
   class Chef
     class OpenIDRegistration
       
       attr_accessor :name, :salt, :validated, :password, :couchdb_rev, :admin
       
       include Chef::Mixin::ParamsValidate
       include Chef::IndexQueue::Indexable
       
       DESIGN_DOCUMENT = {
         "version" => 3,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
               function(doc) {
                 if (doc.chef_type == "openid_registration") {
                   emit(doc.name, doc);
                 }
               }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "openid_registration") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           },
           "validated" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "openid_registration") {
                 if (doc.validated == true) {
                   emit(doc.name, doc);
                 }
               }
             }
             EOJS
           },
           "unvalidated" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "openid_registration") {
                 if (doc.validated == false) {
                   emit(doc.name, doc);
                 }
               }
             }
             EOJS
           },
         },
       }
       
       # Create a new Chef::OpenIDRegistration object.
       def initialize()
         @name = nil
         @salt = nil
         @password = nil
         @validated = false
         @admin = false
         @couchdb_rev = nil
         @couchdb = Chef::CouchDB.new
       end
       
       def name=(n)
         @name = n.gsub(/\./, '_')
       end
       
       # Set the password for this object.
       def set_password(password) 
         @salt = generate_salt
         @password = encrypt_password(@salt, password)      
       end
       
       # Serialize this object as a hash 
       def to_json(*a)
         attributes = Hash.new
         recipes = Array.new
         result = {
           'name' => @name,
           'json_class' => self.class.name,
           'salt' => @salt,
           'password' => @password,
           'validated' => @validated,
           'admin' => @admin,
           'chef_type' => 'openid_registration',
         }
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result.to_json(*a)
       end
       
       # Create a Chef::Node from JSON
       def self.json_create(o)
         me = new
         me.name = o["name"]
         me.salt = o["salt"]
         me.password = o["password"]
         me.validated = o["validated"]
         me.admin = o["admin"]
         me.couchdb_rev = o["_rev"] if o.has_key?("_rev")
         me
       end
       
       # List all the Chef::OpenIDRegistration objects in the CouchDB.  If inflate is set to true, you will get
       # the full list of all registration objects.  Otherwise, you'll just get the IDs
       def self.list(inflate=false)
         rs = Chef::CouchDB.new.list("registrations", inflate)
         if inflate
           rs["rows"].collect { |r| r["value"] }
         else
           rs["rows"].collect { |r| r["key"] }
         end
       end
       
       def self.cdb_list(*args)
         list(*args)
       end
       
       # Load an OpenIDRegistration by name from CouchDB
       def self.load(name)
         Chef::CouchDB.new.load("openid_registration", name)
       end
       
       # Whether or not there is an OpenID Registration with this key.
       def self.has_key?(name)
         Chef::CouchDB.new.has_key?("openid_registration", name)
       end
       
       # Remove this OpenIDRegistration from the CouchDB
       def destroy
         @couchdb.delete("openid_registration", @name, @couchdb_rev)
       end
       
       # Save this OpenIDRegistration to the CouchDB
       def save
         results = @couchdb.store("openid_registration", @name, self)
         @couchdb_rev = results["rev"]
       end
       
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         couchdb ||= Chef::CouchDB.new
         couchdb.create_design_document("registrations", DESIGN_DOCUMENT)
       end
       
       protected
       
         def generate_salt
           salt = Time.now.to_s
           chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
           1.upto(30) { |i| salt << chars[rand(chars.size-1)] }
           salt
         end
       
         def encrypt_password(salt, password)
           Digest::SHA1.hexdigest("--#{salt}--#{password}--")
         end
       
     end
   end

lib/chef/knife/client_bulk_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientBulkDelete < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         banner "knife client bulk delete REGEX (options)"
   
         def run
           if name_args.length < 1
             ui.fatal("You must supply a regular expression to match the results against")
             exit 42
           end
           all_clients = Chef::ApiClient.list(true)
   
           matcher = /#{name_args[0]}/
           clients_to_delete = {}
           all_clients.each do |name, client|
             next unless name =~ matcher
             clients_to_delete[client.name] = client
           end
   
           if clients_to_delete.empty?
             ui.info "No clients match the expression /#{name_args[0]}/"
             exit 0
           end
   
           ui.msg("The following clients will be deleted:")
           ui.msg("")
           ui.msg(ui.list(clients_to_delete.keys.sort, :columns_down))
           ui.msg("")
           ui.confirm("Are you sure you want to delete these clients")
   
           clients_to_delete.sort.each do |name, client|
             client.destroy
             ui.msg("Deleted client #{name}")
           end
         end
       end
     end
   end
   

lib/chef/cookbook/syntax_check.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/checksum_cache'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Cookbook
       # == Chef::Cookbook::SyntaxCheck
       # Encapsulates the process of validating the ruby syntax of files in Chef
       # cookbooks.
       class SyntaxCheck
         include Chef::Mixin::ShellOut
   
         attr_reader :cookbook_path
   
         # Creates a new SyntaxCheck given the +cookbook_name+ and a +cookbook_path+.
         # If no +cookbook_path+ is given, +Chef::Config.cookbook_path+ is used.
         def self.for_cookbook(cookbook_name, cookbook_path=nil)
           cookbook_path ||= Chef::Config.cookbook_path
           unless cookbook_path
             raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given"
           end
           new(File.join(cookbook_path, cookbook_name.to_s))
         end
   
         # Create a new SyntaxCheck object
         # === Arguments
         # cookbook_path::: the (on disk) path to the cookbook
         def initialize(cookbook_path)
           @cookbook_path = cookbook_path
         end
   
         def cache
           Chef::ChecksumCache.instance
         end
   
         def ruby_files
           Dir[File.join(cookbook_path, '**', '*.rb')]
         end
   
         def untested_ruby_files
           ruby_files.reject do |file|
             if validated?(file)
               Chef::Log.debug("Ruby file #{file} is unchanged, skipping syntax check")
               true
             else
               false
             end
           end
         end
   
         def template_files
           Dir[File.join(cookbook_path, '**', '*.erb')]
         end
   
         def untested_template_files
           template_files.reject do |file| 
             if validated?(file)
               Chef::Log.debug("Template #{file} is unchanged, skipping syntax check")
               true
             else
               false
             end
           end
         end
   
         def validated?(file)
             cache.lookup_checksum(cache_key(file), File.stat(file))
         end
   
         def validated(file)
           cache.generate_checksum(cache_key(file), file, File.stat(file))
         end
   
         def cache_key(file)
           @cache_keys ||= {}
           @cache_keys[file] ||= cache.generate_key(file, "chef-test")
         end
   
         def validate_ruby_files
           untested_ruby_files.each do |ruby_file|
             return false unless validate_ruby_file(ruby_file)
             validated(ruby_file)
           end
         end
   
         def validate_templates
           untested_template_files.each do |template|
             return false unless validate_template(template)
             validated(template)
           end
         end
   
         def validate_template(erb_file)
           Chef::Log.debug("Testing template #{erb_file} for syntax errors...")
           result = shell_out("erubis -x #{erb_file} | ruby -c")
           result.error!
           true
         rescue Chef::Exceptions::ShellCommandFailed
           file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
           Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:")
           result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
           false
         end
         
         def validate_ruby_file(ruby_file)
           Chef::Log.debug("Testing #{ruby_file} for syntax errors...")
           result = shell_out("ruby -c #{ruby_file}")
           result.error!
           true
         rescue Chef::Exceptions::ShellCommandFailed
           file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
           Chef::Log.fatal("Cookbook file #{file_relative_path} has a ruby syntax error:")
           result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
           false
         end
         
       end
     end
   end

lib/chef/shef/shef_session.rb

   #--
   # Author:: Daniel DeLeo ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/recipe'
   require 'chef/run_context'
   require 'chef/config'
   require 'chef/client'
   require 'chef/cookbook/cookbook_collection'
   require 'chef/cookbook_loader'
   require 'chef/run_list/run_list_expansion'
   
   module Shef
     class ShefSession
       include Singleton
   
       def self.session_type(type=nil)
         @session_type = type if type
         @session_type
       end
   
       attr_accessor :node, :compile, :recipe, :run_context
       attr_reader :node_attributes, :client
       def initialize
         @node_built = false
       end
   
       def node_built?
           @node_built
       end
   
       def reset!
         loading do
           rebuild_node
           @node = client.node
           shorten_node_inspect
           Shef::Extensions.extend_context_node(@node)
           rebuild_context
           node.consume_attributes(node_attributes) if node_attributes
           @recipe = Chef::Recipe.new(nil, nil, run_context)
           Shef::Extensions.extend_context_recipe(@recipe)
           @node_built = true
         end
       end
   
       def node_attributes=(attrs)
         @node_attributes = attrs
         @node.consume_attributes(@node_attributes)
       end
   
       def resource_collection
         run_context.resource_collection
       end
   
       def run_context
         @run_context ||= rebuild_context
       end
   
       def definitions
         nil
       end
   
       def cookbook_loader
         nil
       end
   
       def save_node
         raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run shef in client mode?"
       end
   
       def rebuild_context
         raise "Not Implemented! :rebuild_collection should be implemented by subclasses"
       end
   
       private
   
       def loading
         show_loading_progress
         begin
           yield
         rescue => e
           loading_complete(false)
           raise e
         else
           loading_complete(true)
         end
       end
   
       def show_loading_progress
         print "Loading"
         @loading = true
         @dot_printer = Thread.new do
           while @loading
             print "."
             sleep 0.5
           end
         end
       end
   
       def loading_complete(success)
         @loading = false
         @dot_printer.join
         msg = success ? "done.\n\n" : "epic fail!\n\n"
         print msg
       end
   
       def shorten_node_inspect
         def @node.inspect
           ""
         end
       end
   
       def rebuild_node
         raise "Not Implemented! :rebuild_node should be implemented by subclasses"
       end
   
     end
   
     class StandAloneSession < ShefSession
   
       session_type :standalone
   
       def rebuild_context
         @run_context = Chef::RunContext.new(@node, {}) # no recipes
         @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", [])) # empty recipe list
       end
   
       private
   
       def rebuild_node
         Chef::Config[:solo] = true
         @client = Chef::Client.new
         @client.run_ohai
         @client.build_node
       end
   
     end
   
     class SoloSession < ShefSession
   
       session_type :solo
   
       def definitions
         @run_context.definitions
       end
   
       def rebuild_context
         @run_status = Chef::RunStatus.new(@node)
         Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) }
         @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new(Chef::CookbookLoader.new(Chef::Config[:cookbook_path])))
         @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", []))
         @run_status.run_context = run_context
       end
   
       private
   
       def rebuild_node
         # Tell the client we're chef solo so it won't try to contact the server
         Chef::Config[:solo] = true
         @client = Chef::Client.new
         @client.run_ohai
         @client.build_node
       end
   
     end
   
     class ClientSession < SoloSession
   
       session_type :client
   
       def save_node
         @client.save_node
       end
   
       def rebuild_context
         @run_status = Chef::RunStatus.new(@node)
         Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, Chef::REST.new(Chef::Config[:server_url])) }
         cookbook_hash = @client.sync_cookbooks
         @run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(cookbook_hash))
         @run_context.load(Chef::RunList::RunListExpansionFromAPI.new("_default", []))
         @run_status.run_context = run_context
       end
   
       private
   
       def rebuild_node
         # Make sure the client knows this is not chef solo
         Chef::Config[:solo] = false
         @client = Chef::Client.new
         @client.run_ohai
         @client.register
         @client.build_node
       end
   
     end
   
     class DoppelGangerClient < Chef::Client
   
       attr_reader :node_name
   
       def initialize(node_name)
         @node_name = node_name
         @ohai = Ohai::System.new
       end
   
       # Run the very smallest amount of ohai we can get away with and still
       # hope to have things work. Otherwise we're not very good doppelgangers
       def run_ohai
         @ohai.require_plugin('os')
       end
   
       # DoppelGanger implementation of build_node. preserves as many of the node's
       # attributes, and does not save updates to the server
       def build_node
         Chef::Log.debug("Building node object for #{@node_name}")
   
         @node = Chef::Node.find_or_create(node_name)
   
         ohai_data = @ohai.data.merge(@node.automatic_attrs)
   
         @node.consume_external_attrs(ohai_data,nil)
   
         @node
       end
   
       def register
         @rest = Chef::REST.new(Chef::Config[:chef_server_url], Chef::Config[:node_name], Chef::Config[:client_key])
       end
   
     end
   
     class DoppelGangerSession < ClientSession
   
       session_type "doppelganger client"
   
       def save_node
         puts "A doppelganger should think twice before saving the node"
       end
   
       def assume_identity(node_name)
         Chef::Config[:doppelganger] = @node_name = node_name
         reset!
       rescue Exception => e
         puts "#{e.class.name}: #{e.message}"
         puts Array(e.backtrace).join("\n")
         puts
         puts "* " * 40
         puts "failed to assume the identity of node '#{node_name}', resetting"
         puts "* " * 40
         puts
         Chef::Config[:doppelganger] = false
         @node_built = false
         Shef.session
       end
   
       def rebuild_node
         # Make sure the client knows this is not chef solo
         Chef::Config[:solo] = false
         @client = DoppelGangerClient.new(@node_name)
         @client.run_ohai
         @client.register
         @client.build_node
   
         @client.sync_cookbooks
       end
   
     end
   
   end

lib/chef/resource/directory.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Directory < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :directory
           @path = name
           @action = :create
           @recursive = false
           @allowed_actions.push(:create, :delete)
         end
         
         def recursive(arg=nil)
           set_or_return(
             :recursive,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
         
         def group(arg=nil)
           set_or_return(
             :group,
             arg,
             :regex => Chef::Config[:group_valid_regex]
           )
         end
         
         def mode(arg=nil)
           set_or_return(
             :mode,
             arg,
             :regex => /^\d{3,4}$/
           )
         end
         
         def owner(arg=nil)
           set_or_return(
             :owner,
             arg,
             :regex => Chef::Config[:user_valid_regex]
           )
         end
         
         def path(arg=nil)
           set_or_return(
             :path,
             arg,
             :kind_of => String
           )
         end
         
       end
     end
   end

lib/chef/provider/directory.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/log'
   require 'chef/resource/directory'
   require 'chef/provider'
   require 'chef/provider/file'
   require 'fileutils'
   
   class Chef
     class Provider
       class Directory < Chef::Provider::File
         def load_current_resource
           @current_resource = Chef::Resource::Directory.new(@new_resource.name)
           @current_resource.path(@new_resource.path)
           if ::File.exist?(@current_resource.path) && ::File.directory?(@current_resource.path)
             cstats = ::File.stat(@current_resource.path)
             @current_resource.owner(cstats.uid)
             @current_resource.group(cstats.gid)
             @current_resource.mode("%o" % (cstats.mode & 007777))
           end
           @current_resource
         end
   
         def action_create
           unless ::File.exists?(@new_resource.path)
             if @new_resource.recursive == true
               ::FileUtils.mkdir_p(@new_resource.path)
             else
               ::Dir.mkdir(@new_resource.path)
             end
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} created directory #{@new_resource.path}")
           end
           set_owner if @new_resource.owner != nil
           set_group if @new_resource.group != nil
           set_mode if @new_resource.mode != nil
         end
   
         def action_delete
           if ::File.directory?(@new_resource.path) && ::File.writable?(@new_resource.path)
             if @new_resource.recursive == true
               FileUtils.rm_rf(@new_resource.path)
               Chef::Log.info("#{@new_resource} deleted #{@new_resource.path} recursively")
             else
               ::Dir.delete(@new_resource.path)
               Chef::Log.info("#{@new_resource} deleted #{@new_resource.path}")
             end
             @new_resource.updated_by_last_action(true)
           else
             raise RuntimeError, "Cannot delete #{@new_resource} at #{@new_resource_path}!" if ::File.exists?(@new_resource.path)
           end
         end
       end
     end
   end

lib/chef/data_bag.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/couchdb'
   require 'chef/data_bag_item'
   require 'chef/index_queue'
   require 'chef/mash'
   require 'chef/json_compat'
   
   class Chef
     class DataBag
   
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       include Chef::IndexQueue::Indexable
   
       VALID_NAME = /^[\-[:alnum:]_]+$/
   
       DESIGN_DOCUMENT = {
         "version" => 2,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "data_bag") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "data_bag") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           },
           "entries" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "data_bag_item") {
                 emit(doc.data_bag, doc.raw_data.id);
               }
             }
             EOJS
           }
         }
       }
   
       def self.validate_name!(name)
         unless name =~ VALID_NAME
           raise Exceptions::InvalidDataBagName, "DataBags must have a name matching #{VALID_NAME.inspect}, you gave #{name.inspect}"
         end
       end
   
       attr_accessor :couchdb_rev, :couchdb_id, :couchdb
   
       # Create a new Chef::DataBag
       def initialize(couchdb=nil)
         @name = ''
         @couchdb_rev = nil
         @couchdb_id = nil
         @couchdb = (couchdb || Chef::CouchDB.new)
       end
   
       def name(arg=nil)
         set_or_return(
           :name,
           arg,
           :regex => VALID_NAME
         )
       end
   
       def to_hash
         result = {
           "name" => @name,
           'json_class' => self.class.name,
           "chef_type" => "data_bag",
         }
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result
       end
   
       # Serialize this object as a hash
       def to_json(*a)
         to_hash.to_json(*a)
       end
   
       def chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def self.chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       # Create a Chef::Role from JSON
       def self.json_create(o)
         bag = new
         bag.name(o["name"])
         bag.couchdb_rev = o["_rev"] if o.has_key?("_rev")
         bag.couchdb_id = o["_id"] if o.has_key?("_id")
         bag.index_id = bag.couchdb_id
         bag
       end
   
       # List all the Chef::DataBag objects in the CouchDB.  If inflate is set to true, you will get
       # the full list of all Roles, fully inflated.
       def self.cdb_list(inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).list("data_bags", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }
       end
   
       def self.list(inflate=false)
         if inflate
           # Can't search for all data bags like other objects, fall back to N+1 :(
           list(false).inject({}) do |response, bag_and_uri|
             response[bag_and_uri.first] = load(bag_and_uri.first)
             response
           end
         else
           Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data")
         end
       end
   
       # Load a Data Bag by name from CouchDB
       def self.cdb_load(name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("data_bag", name)
       end
   
       # Load a Data Bag by name via either the RESTful API or local data_bag_path if run in solo mode
       def self.load(name)
         if Chef::Config[:solo]
           unless File.directory?(Chef::Config[:data_bag_path])
             raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{Chef::Config[:data_bag_path]}' is invalid"
           end
   
           Dir.glob(File.join(Chef::Config[:data_bag_path], name, "*.json")).inject({}) do |bag, f|
             item = JSON.parse(IO.read(f))
             bag[item['id']] = item
             bag
           end
         else
           Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data/#{name}")
         end
       end
   
       # Remove this Data Bag from CouchDB
       def cdb_destroy
         removed = @couchdb.delete("data_bag", @name, @couchdb_rev)
         rs = @couchdb.get_view("data_bags", "entries", :include_docs => true, :startkey => @name, :endkey => @name)
         rs["rows"].each do |row|
           row["doc"].couchdb = couchdb
           row["doc"].cdb_destroy
         end
         removed
       end
   
       def destroy
         chef_server_rest.delete_rest("data/#{@name}")
       end
   
       # Save this Data Bag to the CouchDB
       def cdb_save
         results = @couchdb.store("data_bag", @name, self)
         @couchdb_rev = results["rev"]
       end
   
       # Save the Data Bag via RESTful API
       def save
         begin
           chef_server_rest.put_rest("data/#{@name}", self)
         rescue Net::HTTPServerException => e
           raise e unless e.response.code == "404"
           chef_server_rest.post_rest("data", self)
         end
         self
       end
   
       #create a data bag via RESTful API
       def create
         chef_server_rest.post_rest("data", self)
         self
       end
   
       # List all the items in this Bag from CouchDB
       # The self.load method does this through the REST API
       def list(inflate=false)
         rs = nil
         if inflate
           rs = @couchdb.get_view("data_bags", "entries", :include_docs => true, :startkey => @name, :endkey => @name)
           rs["rows"].collect { |r| r["doc"] }
         else
           rs = @couchdb.get_view("data_bags", "entries", :startkey => @name, :endkey => @name)
           rs["rows"].collect { |r| r["value"] }
         end
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("data_bags", DESIGN_DOCUMENT)
       end
   
       # As a string
       def to_s
         "data_bag[#{@name}]"
       end
   
     end
   end
   

lib/chef/mixin/recipe_definition_dsl_core.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   require 'chef/mixin/convert_to_class_name'
   require 'chef/mixin/language'
   
   #--
   # UGH. this is a circular require that will cause an uninitialized constant
   # error, but this file really does depend on Chef::Recipe. oh well.
   # require 'chef/recipe'
   
   class Chef
     module Mixin
       module RecipeDefinitionDSLCore
         
         include Chef::Mixin::ConvertToClassName
         include Chef::Mixin::Language
         
         def method_missing(method_symbol, *args, &block)
           # If we have a definition that matches, we want to use that instead.  This should
           # let you do some really crazy over-riding of "native" types, if you really want
           # to. 
           if run_context.definitions.has_key?(method_symbol)
             # This dupes the high level object, but we still need to dup the params
             new_def = run_context.definitions[method_symbol].dup
             new_def.params = new_def.params.dup
             new_def.node = run_context.node
             # This sets up the parameter overrides
             new_def.instance_eval(&block) if block
             new_recipe = Chef::Recipe.new(cookbook_name, @recipe_name, run_context)
             new_recipe.params = new_def.params
             new_recipe.params[:name] = args[0]
             new_recipe.instance_eval(&new_def.recipe)
           else
             # Otherwise, we're rocking the regular resource call route.
             method_name = method_symbol.to_s
             rname = convert_to_class_name(method_name)
   
             super unless Chef::Resource.const_defined?(rname)
             raise ArgumentError, "You must supply a name when declaring a #{method_name} resource" unless args.size > 0
   
             # If we have a resource like this one, we want to steal its state
             args << run_context
             resource = Chef::Resource.const_get(rname).new(*args)
             resource.load_prior_resource
             resource.cookbook_name = cookbook_name
             resource.recipe_name = @recipe_name
             resource.params = @params
             resource.source_line = caller[0]
             # Determine whether this resource is being created in the context of an enclosing Provider
             resource.enclosing_provider = self.is_a?(Chef::Provider) ? self : nil
             resource.instance_eval(&block) if block
   
             run_context.resource_collection.insert(resource)
             resource
           end
         end
         
       end
     end
   end

lib/chef/daemon.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   # I love you Merb (lib/merb-core/server.rb)
   
   require 'chef/config'
   require 'etc'
   
   class Chef
     class Daemon
       class << self
         attr_accessor :name
   
         # Daemonize the current process, managing pidfiles and process uid/gid
         #
         # === Parameters
         # name:: The name to be used for the pid file
         #
         def daemonize(name)
           @name = name
           pid = pid_from_file
           unless running?
             remove_pid_file()
             Chef::Log.info("Daemonizing..")
             begin
               exit if fork
               Process.setsid
               exit if fork
               Chef::Log.info("Forked, in #{Process.pid}. Priveleges: #{Process.euid} #{Process.egid}")
               File.umask Chef::Config[:umask]
               $stdin.reopen("/dev/null")
               $stdout.reopen("/dev/null", "a")
               $stderr.reopen($stdout)
               save_pid_file
               at_exit { remove_pid_file }
             rescue NotImplementedError => e
               Chef::Application.fatal!("There is no fork: #{e.message}")
             end
           else
             Chef::Application.fatal!("Chef is already running pid #{pid}")
           end
         end
     
         # Check if Chef is running based on the pid_file
         # ==== Returns
         # Boolean::
         # True if Chef is running
         # False if Chef is not running
         #
         def running?
           if pid_from_file.nil?
             false
           else
             Process.kill(0, pid_from_file)
             true
           end
         rescue Errno::ESRCH, Errno::ENOENT
           false
         rescue Errno::EACCES => e
           Chef::Application.fatal!("You don't have access to the PID file at #{pid_file}: #{e.message}")
         end
         
         # Gets the pid file for @name
         # ==== Returns
         # String::
         #   Location of the pid file for @name
         def pid_file
            Chef::Config[:pid_file] or "/tmp/#{@name}.pid"
         end
         
         # Suck the pid out of pid_file
         # ==== Returns
         # Integer::
         #   The PID from pid_file
         # nil::
         #   Returned if the pid_file does not exist.
         #
         def pid_from_file
           File.read(pid_file).chomp.to_i
         rescue Errno::ENOENT, Errno::EACCES
           nil
         end
       
         # Store the PID on the filesystem
         # This uses the Chef::Config[:pid_file] option, or "/tmp/name.pid" otherwise
         #
         def save_pid_file
           file = pid_file
           begin
             FileUtils.mkdir_p(File.dirname(file))
           rescue Errno::EACCES => e
             Chef::Application.fatal!("Failed store pid in #{File.dirname(file)}, permission denied: #{e.message}")
           end
         
           begin
             File.open(file, "w") { |f| f.write(Process.pid.to_s) }
           rescue Errno::EACCES => e
             Chef::Application.fatal!("Couldn't write to pidfile #{file}, permission denied: #{e.message}")
           end
         end
       
         # Delete the PID from the filesystem
         def remove_pid_file
           FileUtils.rm(pid_file) if File.exists?(pid_file)
         end
              
         # Change process user/group to those specified in Chef::Config
         #
         def change_privilege
           Dir.chdir("/")
   
           if Chef::Config[:user] and Chef::Config[:group]
             Chef::Log.info("About to change privilege to #{Chef::Config[:user]}:#{Chef::Config[:group]}")
             _change_privilege(Chef::Config[:user], Chef::Config[:group])
           elsif Chef::Config[:user]
             Chef::Log.info("About to change privilege to #{Chef::Config[:user]}")
             _change_privilege(Chef::Config[:user])
           end
         end
       
         # Change privileges of the process to be the specified user and group
         #
         # ==== Parameters
         # user:: The user to change the process to.
         # group:: The group to change the process to.
         #
         # ==== Alternatives
         # If group is left out, the user will be used (changing to user:user)
         #
         def _change_privilege(user, group=user)
           uid, gid = Process.euid, Process.egid
   
           begin
             target_uid = Etc.getpwnam(user).uid
           rescue ArgumentError => e
             Chef::Application.fatal!("Failed to get UID for user #{user}, does it exist? #{e.message}")
             return false
           end
      
           begin
             target_gid = Etc.getgrnam(group).gid
           rescue ArgumentError => e
             Chef::Application.fatal!("Failed to get GID for group #{group}, does it exist? #{e.message}")
             return false
           end
         
           if (uid != target_uid) or (gid != target_gid)
             Process.initgroups(user, target_gid)
             Process::GID.change_privilege(target_gid)
             Process::UID.change_privilege(target_uid)
           end
           true
         rescue Errno::EPERM => e
           Chef::Application.fatal!("Permission denied when trying to change #{uid}:#{gid} to #{target_uid}:#{target_gid}. #{e.message}")
         end
       end
     end
   end

lib/chef/checksum_cache.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'set'
   require 'fileutils'
   require 'chef/log'
   require 'chef/config'
   require 'chef/client'
   require 'chef/mixin/convert_to_class_name'
   require 'singleton'
   require 'moneta'
   
   class Chef
     class ChecksumCache
       include Chef::Mixin::ConvertToClassName
       include ::Singleton
   
       attr_reader :moneta
   
       def initialize(*args)
         self.reset!(*args)
       end
   
       def reset!(backend=nil, options=nil)
         backend ||= Chef::Config[:cache_type]
         options ||= Chef::Config[:cache_options]
   
         begin
           require "moneta/#{convert_to_snake_case(backend, 'Moneta')}"
         rescue LoadError => e
           Chef::Log.fatal("Could not load Moneta back end #{backend.inspect}")
           raise e
         end
   
         @moneta = Moneta.const_get(backend).new(options)
       end
   
       def self.reset_cache_validity
         @valid_cached_checksums = nil
       end
   
       Chef::Client.when_run_starts do |run_status|
         reset_cache_validity
       end
   
       def self.valid_cached_checksums
         @valid_cached_checksums ||= Set.new
       end
   
       def self.validate_checksum(checksum_key)
         valid_cached_checksums << checksum_key
       end
   
       def self.all_cached_checksums
         all_checksums_with_filenames = {}
   
         Dir[File.join(Chef::Config[:cache_options][:path], '*')].each do |cksum_file|
           all_checksums_with_filenames[File.basename(cksum_file)] = cksum_file
         end
         all_checksums_with_filenames
       end
   
       def self.cleanup_checksum_cache
         Chef::Log.debug("Cleaning the checksum cache")
         if (Chef::Config[:cache_type].to_s == "BasicFile")
           all_cached_checksums.each do |cache_key, cksum_cache_file|
             unless valid_cached_checksums.include?(cache_key)
               remove_unused_checksum(cksum_cache_file)
             end
           end
         end
       end
   
       Chef::Client.when_run_completes_successfully do |run_status|
         cleanup_checksum_cache
       end
   
       def self.remove_unused_checksum(checksum_file)
         Chef::Log.debug("Removing unused checksum cache file #{checksum_file}")
         FileUtils.rm(checksum_file)
       end
   
       def self.checksum_for_file(*args)
         instance.checksum_for_file(*args)
       end
   
       def validate_checksum(*args)
         self.class.validate_checksum(*args)
       end
   
       def checksum_for_file(file, key=nil)
         key ||= generate_key(file)
         fstat = File.stat(file)
         lookup_checksum(key, fstat) || generate_checksum(key, file, fstat)
       end
   
       def lookup_checksum(key, fstat)
         cached = fetch(key)
         if cached && file_unchanged?(cached, fstat)
           validate_checksum(key)
           cached["checksum"]
         else
           nil
         end
       end
   
       def generate_checksum(key, file, fstat)
         checksum = checksum_file(file, Digest::SHA256.new)
         moneta.store(key, {"mtime" => fstat.mtime.to_f, "checksum" => checksum})
         validate_checksum(key)
         checksum
       end
   
       def generate_key(file, group="chef")
         "#{group}-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
       end
   
       def self.generate_md5_checksum_for_file(*args)
         instance.generate_md5_checksum_for_file(*args)
       end
   
       def generate_md5_checksum_for_file(file)
         checksum_file(file, Digest::MD5.new)
       end
   
       def generate_md5_checksum(io)
         checksum_io(io, Digest::MD5.new)
       end
   
       private
   
       def fetch(key)
         @moneta.fetch(key)
       rescue ArgumentError => e
         Log.warn "Error loading cached checksum for key #{key.inspect}"
         Log.warn(e)
         repair_checksum_cache
         nil
       end
   
       def repair_checksum_cache
         Chef::Log.info("Removing invalid checksum cache files")
         Dir["#{Chef::Config[:cache_options][:path]}/*"].each do |file_path|
           File.unlink(file_path) unless File.size?(file_path)
         end
       end
   
       def file_unchanged?(cached, fstat)
         cached["mtime"].to_f == fstat.mtime.to_f
       end
   
       def checksum_file(file, digest)
         File.open(file, 'rb') { |f| checksum_io(f, digest) }
       end
   
       def checksum_io(io, digest)
         while chunk = io.read(1024 * 8)
           digest.update(chunk)
         end
         digest.hexdigest
       end
   
     end
   end
   
   module Moneta
     module Defaults
       def default
         nil
       end
     end
   end

lib/chef/knife/data_bag_create.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class DataBagCreate < Knife
   
         deps do
           require 'chef/data_bag'
           require 'chef/encrypted_data_bag_item'
         end
   
         banner "knife data bag create BAG [ITEM] (options)"
         category "data bag"
   
         option :secret,
         :short => "-s SECRET",
         :long  => "--secret ",
         :description => "The secret key to use to encrypt data bag item values"
   
         option :secret_file,
         :long => "--secret-file SECRET_FILE",
         :description => "A file containing the secret key to use to encrypt data bag item values"
   
         def read_secret
           if config[:secret]
             config[:secret]
           else
             Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
           end
         end
   
         def use_encryption
           if config[:secret] && config[:secret_file]
             ui.fatal("please specify only one of --secret, --secret-file")
             exit(1)
           end
           config[:secret] || config[:secret_file]
         end
   
         def run
           @data_bag_name, @data_bag_item_name = @name_args
   
           if @data_bag_name.nil?
             show_usage
             ui.fatal("You must specify a data bag name")
             exit 1
           end
   
           # create the data bag
           begin
             rest.post_rest("data", { "name" => @data_bag_name })
             ui.info("Created data_bag[#{@data_bag_name}]")
           rescue Net::HTTPServerException => e
             raise unless e.to_s =~ /^409/
             ui.info("Data bag #{@data_bag_name} already exists")
           end
   
           # if an item is specified, create it, as well
           if @data_bag_item_name
             create_object({ "id" => @data_bag_item_name }, "data_bag_item[#{@data_bag_item_name}]") do |output|
               item = Chef::DataBagItem.from_hash(
                        if use_encryption
                          Chef::EncryptedDataBagItem.encrypt_data_bag_item(output, read_secret)
                        else
                          output
                        end)
               item.data_bag(@data_bag_name)
               rest.post_rest("data/#{@data_bag_name}", item)
             end
           end
         end
       end
     end
   end

lib/chef/solr_query/solr_http_request.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'net/http'
   require 'uri'
   require 'chef/json_compat'
   require 'chef/config'
   
   class Chef
     class SolrQuery
       class SolrHTTPRequest
         CLASS_FOR_METHOD = {:GET => Net::HTTP::Get, :POST => Net::HTTP::Post}
   
         UPDATE_URL = '/solr/update'
         TEXT_XML = {"Content-Type" => "text/xml"}
   
         def self.solr_url=(solr_url)
           @solr_url = solr_url
           @http_client = nil
         end
   
         def self.solr_url
           @solr_url || Chef::Config[:solr_url]
         end
   
         def self.http_client
           @http_client ||= begin
             uri = URI.parse(solr_url)
             Net::HTTP.new(uri.host, uri.port)
           end
         end
   
         def self.select(params={})
           url = "/solr/select?#{url_join(params)}"
           Chef::Log.debug("Sending #{url} to Solr")
           request = new(:GET, url)
           json_response = request.run("Search Query to Solr '#{solr_url}#{url}'")
           Chef::JSONCompat.from_json(json_response)
         end
   
         def self.update(doc)
           Chef::Log.debug("POSTing document to SOLR:\n#{doc}")
           request = new(:POST, UPDATE_URL, TEXT_XML) { |req| req.body = doc.to_s }
           request.run("POST to Solr '#{UPDATE_URL}', data: #{doc}")
         end
   
         def self.url_join(params_hash={})
           params = params_hash.inject("") do |param_str, params|
             param_str << "#{params[0]}=#{escape(params[1])}&"
           end
           params.chop! # trailing &
           params
         end
   
         def self.escape(s)
           s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
             '%'+$1.unpack('H2'*$1.size).join('%').upcase
           }.tr(' ', '+')
         end
   
         def initialize(method, url, headers=nil)
           args = headers ? [url, headers] : url
           @request = CLASS_FOR_METHOD[method].new(*args)
           yield @request if block_given?
         end
   
         def http_client
           self.class.http_client
         end
   
         def solr_url
           self.class.solr_url
         end
   
         def run(description="HTTP Request to Solr")
           response = http_client.request(@request)
           request_failed!(response, description) unless response.kind_of?(Net::HTTPSuccess)
           response.body
         rescue NoMethodError => e
           # http://redmine.ruby-lang.org/issues/show/2708
           # http://redmine.ruby-lang.org/issues/show/2758
           if e.to_s =~ /#{Regexp.escape(%q|undefined method 'closed?' for nil:NilClass|)}/
             Chef::Log.fatal("#{description} failed.  Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED (net/http undefined method closed?) attempting to contact #{solr_url}")
             Chef::Log.debug("Rescued error in http connect, treating it as Errno::ECONNREFUSED to hide bug in net/http")
             Chef::Log.debug(e.backtrace.join("\n"))
             raise Chef::Exceptions::SolrConnectionError, "Errno::ECONNREFUSED: Connection refused attempting to contact #{solr_url}"
           else
             raise
           end
         end
   
         def request_failed!(response, description='HTTP call')
           Chef::Log.fatal("#{description} failed (#{response.class} #{response.code} #{response.message})")
           response.error!
         rescue Timeout::Error, Errno::EINVAL, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT => e
           Chef::Log.debug(e.backtrace.join("\n"))
           raise Chef::Exceptions::SolrConnectionError, "#{e.class.name}: #{e.to_s}"
         end
   
       end
     end
   end

lib/chef/data_bag_item.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'forwardable'
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/couchdb'
   require 'chef/index_queue'
   require 'chef/data_bag'
   require 'chef/mash'
   require 'chef/json_compat'
   
   class Chef
     class DataBagItem
   
       extend Forwardable
   
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       include Chef::IndexQueue::Indexable
   
       VALID_ID = /^[\-[:alnum:]_]+$/
   
       DESIGN_DOCUMENT = {
         "version" => 1,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "data_bag_item") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "data_bag_item") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           }
         }
       }
   
       def self.validate_id!(id_str)
         if id_str.nil? || ( id_str !~ VALID_ID )
           raise Exceptions::InvalidDataBagItemID, "Data Bag items must have an id matching #{VALID_ID.inspect}, you gave: #{id_str.inspect}"
         end
       end
   
       # Define all Hash's instance methods as delegating to @raw_data
       def_delegators(:@raw_data, *(Hash.instance_methods - Object.instance_methods))
   
       attr_accessor :couchdb_rev, :couchdb_id, :couchdb
       attr_reader :raw_data
   
       # Create a new Chef::DataBagItem
       def initialize(couchdb=nil)
         @couchdb_rev = nil
         @couchdb_id = nil
         @data_bag = nil
         @raw_data = Mash.new
         @couchdb = couchdb || Chef::CouchDB.new
       end
   
       def chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def self.chef_server_rest
         Chef::REST.new(Chef::Config[:chef_server_url])
       end
   
       def raw_data
         @raw_data
       end
   
       def validate_id!(id_str)
         self.class.validate_id!(id_str)
       end
   
       def raw_data=(new_data)
         unless new_data.respond_to?(:[]) && new_data.respond_to?(:keys)
           raise Exceptions::ValidationFailed, "Data Bag Items must contain a Hash or Mash!"
         end
         validate_id!(new_data["id"])
         @raw_data = new_data
       end
   
       def data_bag(arg=nil)
         set_or_return(
           :data_bag,
           arg,
           :regex => /^[\-[:alnum:]_]+$/
         )
       end
   
       def name
         object_name
       end
   
       def object_name
         raise Exceptions::ValidationFailed, "You must have an 'id' or :id key in the raw data" unless raw_data.has_key?('id')
         raise Exceptions::ValidationFailed, "You must have declared what bag this item belongs to!" unless data_bag
   
         id = raw_data['id']
         "data_bag_item_#{data_bag}_#{id}"
       end
   
       def self.object_name(data_bag_name, id)
         "data_bag_item_#{data_bag_name}_#{id}"
       end
   
       def to_hash
         result = self.raw_data
         result["chef_type"] = "data_bag_item"
         result["data_bag"] = self.data_bag
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result
       end
   
       # Serialize this object as a hash
       def to_json(*a)
         result = {
           "name" => self.object_name,
           "json_class" => self.class.name,
           "chef_type" => "data_bag_item",
           "data_bag" => self.data_bag,
           "raw_data" => self.raw_data
         }
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result.to_json(*a)
       end
   
       def self.from_hash(h)
         item = new
         item.raw_data = h
         item
       end
   
       # Create a Chef::DataBagItem from JSON
       def self.json_create(o)
         bag_item = new
         bag_item.data_bag(o["data_bag"])
         o.delete("data_bag")
         o.delete("chef_type")
         o.delete("json_class")
         o.delete("name")
         if o.has_key?("_rev")
           bag_item.couchdb_rev = o["_rev"]
           o.delete("_rev")
         end
         if o.has_key?("_id")
           bag_item.couchdb_id = o["_id"]
           bag_item.index_id = bag_item.couchdb_id
           o.delete("_id")
         end
         bag_item.raw_data = Mash.new(o["raw_data"])
         bag_item
       end
   
       # Load a Data Bag Item by name from CouchDB
       def self.cdb_load(data_bag, name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("data_bag_item", object_name(data_bag, name))
       end
   
       # Load a Data Bag Item by name via either the RESTful API or local data_bag_path if run in solo mode
       def self.load(data_bag, name)
         if Chef::Config[:solo]
           bag = Chef::DataBag.load(data_bag)
           item = bag[name]
         else
           item = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data/#{data_bag}/#{name}")
         end
   
         if item.kind_of?(DataBagItem)
           item
         else
           item = from_hash(item)
           item.data_bag(data_bag)
           item
         end
       end
   
       # Remove this Data Bag Item from CouchDB
       def cdb_destroy
         Chef::Log.debug "Destroying data bag item: #{self.inspect}"
         @couchdb.delete("data_bag_item", object_name, @couchdb_rev)
       end
   
       def destroy(data_bag=data_bag, databag_item=name)
         chef_server_rest.delete_rest("data/#{data_bag}/#{databag_item}")
       end
   
       # Save this Data Bag Item to CouchDB
       def cdb_save
         @couchdb_rev = @couchdb.store("data_bag_item", object_name, self)["rev"]
       end
   
       # Save this Data Bag Item via RESTful API
       def save(item_id=@raw_data['id'])
         r = chef_server_rest
         begin
           r.put_rest("data/#{data_bag}/#{item_id}", self)
         rescue Net::HTTPServerException => e
           raise e unless e.response.code == "404"
           r.post_rest("data/#{data_bag}", self)
         end
         self
       end
   
       # Create this Data Bag Item via RESTful API
       def create
         chef_server_rest.post_rest("data/#{data_bag}", self)
         self
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("data_bag_items", DESIGN_DOCUMENT)
       end
   
       def ==(other)
         other.respond_to?(:to_hash) &&
         other.respond_to?(:data_bag) &&
         (other.to_hash == to_hash) &&
         (other.data_bag.to_s == data_bag.to_s)
       end
   
       # As a string
       def to_s
         "data_bag_item[#{id}]"
       end
   
       def inspect
         "data_bag_item[#{data_bag.inspect}, #{raw_data['id'].inspect}, #{raw_data.inspect}]"
       end
   
       def pretty_print(pretty_printer)
         pretty_printer.pp({"data_bag_item('#{data_bag}', '#{id}')" => self.to_hash})
       end
   
       def id
         @raw_data['id']
       end
   
     end
   end
   
   

lib/chef/mixin/command.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   require 'chef/exceptions'
   require 'tmpdir'
   require 'fcntl'
   require 'etc'
   
   class Chef
     module Mixin
       module Command
         extend self
   
   
         if RUBY_PLATFORM =~ /mswin|mingw32|windows/
           require 'chef/mixin/command/windows'
           include ::Chef::Mixin::Command::Windows
           extend  ::Chef::Mixin::Command::Windows
         else
           require 'chef/mixin/command/unix'
           include ::Chef::Mixin::Command::Unix
           extend  ::Chef::Mixin::Command::Unix
         end
   
         # If command is a block, returns true if the block returns true, false if it returns false.
         # ("Only run this resource if the block is true")
         #
         # If the command is not a block, executes the command.  If it returns any status other than
         # 0, it returns false (clearly, a 0 status code is true)
         #
         # === Parameters
         # command, :: A block to check, or a string to execute
         #
         # === Returns
         # true:: Returns true if the block is true, or if the command returns 0
         # false:: Returns false if the block is false, or if the command returns a non-zero exit code.
         def only_if(command, args = {})
           if command.kind_of?(Proc)
             chdir_or_tmpdir(args[:cwd]) do
               res = command.call
               unless res
                 return false
               end
             end
           else  
             status = run_command({:command => command, :ignore_failure => true}.merge(args))
             if status.exitstatus != 0
               return false
             end
           end
           true
         end
         
         # If command is a block, returns false if the block returns true, true if it returns false.
         # ("Do not run this resource if the block is true")
         #
         # If the command is not a block, executes the command.  If it returns a 0 exitstatus, returns false.
         # ("Do not run this resource if the command returns 0")
         #
         # === Parameters
         # command, :: A block to check, or a string to execute
         #
         # === Returns
         # true:: Returns true if the block is false, or if the command returns a non-zero exit status.
         # false:: Returns false if the block is true, or if the command returns a 0 exit status.
         def not_if(command, args = {})
           if command.kind_of?(Proc)
             chdir_or_tmpdir(args[:cwd]) do
               res = command.call
               if res
                 return false
               end
             end
           else  
             status = run_command({:command => command, :ignore_failure => true}.merge(args))
             if status.exitstatus == 0
               return false
             end
           end
           true
         end
         
         # === Parameters
         # args: A number of required and optional arguments
         #   command, : A complete command with options to execute or a command and options as an Array 
         #   creates: The absolute path to a file that prevents the command from running if it exists
         #   cwd: Working directory to execute command in, defaults to Dir.tmpdir
         #   timeout: How many seconds to wait for the command to execute before timing out
         #   returns: The single exit value command is expected to return, otherwise causes an exception
         #   ignore_failure: Whether to raise an exception on failure, or just return the status
         #   output_on_failure: Return output in raised exception regardless of Log.level
         # 
         #   user: The UID or user name of the user to execute the command as
         #   group: The GID or group name of the group to execute the command as
         #   environment: Pairs of environment variable names and their values to set before execution
         #
         # === Returns
         # Returns the exit status of args[:command]
         def run_command(args={})         
           command_output = ""
           
           args[:ignore_failure] ||= false
           args[:output_on_failure] ||= false
   
           # TODO: This is the wrong place for this responsibility.
           if args.has_key?(:creates)
             if File.exists?(args[:creates])
               Chef::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.")
               return false
             end
           end
           
           status, stdout, stderr = output_of_command(args[:command], args)
           command_output << "STDOUT: #{stdout}"
           command_output << "STDERR: #{stderr}"
           handle_command_failures(status, command_output, args)
           
           status
         end
         
         def output_of_command(command, args)
           Chef::Log.debug("Executing #{command}")
           stderr_string, stdout_string, status = "", "", nil
           
           exec_processing_block = lambda do |pid, stdin, stdout, stderr|
             stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp
           end
           
           args[:cwd] ||= Dir.tmpdir
           unless ::File.directory?(args[:cwd])
             raise Chef::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory"
           end
           
           Dir.chdir(args[:cwd]) do
             if args[:timeout]
               begin
                 Timeout.timeout(args[:timeout]) do
                   status = popen4(command, args, &exec_processing_block)
                 end
               rescue Timeout::Error => e
                 Chef::Log.error("#{command} exceeded timeout #{args[:timeout]}")
                 raise(e)
               end
             else
               status = popen4(command, args, &exec_processing_block)
             end
             
             Chef::Log.debug("---- Begin output of #{command} ----")
             Chef::Log.debug("STDOUT: #{stdout_string}")
             Chef::Log.debug("STDERR: #{stderr_string}")
             Chef::Log.debug("---- End output of #{command} ----")
             Chef::Log.debug("Ran #{command} returned #{status.exitstatus}")
           end
           
           return status, stdout_string, stderr_string
         end
         
         def handle_command_failures(status, command_output, opts={})
           unless opts[:ignore_failure]
             opts[:returns] ||= 0
             unless Array(opts[:returns]).include?(status.exitstatus)
               # if the log level is not debug, through output of command when we fail
               output = ""
               if Chef::Log.level == :debug || opts[:output_on_failure]
                 output << "\n---- Begin output of #{opts[:command]} ----\n"
                 output << command_output.to_s
                 output << "\n---- End output of #{opts[:command]} ----\n"
               end
               raise Chef::Exceptions::Exec, "#{opts[:command]} returned #{status.exitstatus}, expected #{opts[:returns]}#{output}"
             end
           end
         end
         
         # Call #run_command but set LC_ALL to the system's current environment so it doesn't get changed to C.
         #
         # === Parameters
         # args: A number of required and optional arguments that will be handed out to #run_command
         #
         # === Returns
         # Returns the result of #run_command
         def run_command_with_systems_locale(args={})
           args[:environment] ||= {}
           args[:environment]["LC_ALL"] = ENV["LC_ALL"]
           run_command args
         end
   
         # def popen4(cmd, args={}, &b)
         #   @@os_handler.popen4(cmd, args, &b)
         # end
   
         # module_function :popen4
   
         def chdir_or_tmpdir(dir, &block)
           dir ||= Dir.tmpdir
           unless File.directory?(dir)
             raise Chef::Exceptions::Exec, "#{dir} does not exist or is not a directory"
           end
           Dir.chdir(dir) do
             block.call
           end
         end
   
       end
     end
   end

lib/chef/provider/group/windows.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/user'
   if RUBY_PLATFORM =~ /mswin|mingw32|windows/
     require 'chef/util/windows/net_group'
   end
   
   class Chef
     class Provider
       class Group
         class Windows < Chef::Provider::Group
           
           def initialize(new_resource,run_context)
             super
             @net_group = Chef::Util::Windows::NetGroup.new(@new_resource.name)
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::Group.new(@new_resource.name)
             @current_resource.group_name(@new_resource.group_name)
           
             members = nil
             begin
               members = @net_group.local_get_members
             rescue => e
               @group_exists = false
               Chef::Log.debug("#{@new_resource} group does not exist")
             end
   
             if members
               @current_resource.members(members)
             end
   
             @current_resource
           end
           
           def create_group
             @net_group.local_add
             manage_group
           end
           
           def manage_group
             if @new_resource.append
               begin
                 #ERROR_MEMBER_IN_ALIAS if a member already exists in the group
                 @net_group.local_add_members(@new_resource.members)
               rescue
                 members = @new_resource.members + @current_resource.members
                 @net_group.local_set_members(members.uniq)
               end
             else
               @net_group.local_set_members(@new_resource.members)
             end
           end
           
           def remove_group
             @net_group.local_delete
           end
           
         end
       end
     end
   end

lib/chef/sandbox.rb

   #
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/log'
   require 'uuidtools'
   
   class Chef
     class Sandbox
       attr_accessor :is_completed, :create_time
       alias_method :is_completed?, :is_completed
       attr_reader :guid
       
       alias :name :guid
       
       attr_accessor :couchdb, :couchdb_id, :couchdb_rev
   
       # list of checksum ids
       attr_accessor :checksums
   
       DESIGN_DOCUMENT = {
         "version" => 1,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) { 
               if (doc.chef_type == "sandbox") {
                 emit(doc.guid, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "sandbox") {
                 emit(doc.guid, doc.guid);
               }
             }
             EOJS
           },
           "all_incomplete" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "sandbox" && !doc.is_completed) {
                 emit(doc.guid, doc.guid);
               }
             }
             EOJS
           },
           "all_completed" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "sandbox" && doc.is_completed) {
                 emit(doc.guid, doc.guid);
               }
             }
             EOJS
           },
         }
       }
       
       # Creates a new Chef::Sandbox object.  
       #
       # === Returns
       # object:: Duh. :)
       def initialize(guid=nil, couchdb=nil)
         @guid = guid || UUIDTools::UUID.random_create.to_s.gsub(/\-/,'').downcase
         @is_completed = false
         @create_time = Time.now.iso8601
         @checksums = Array.new
       end
   
       def include?(checksum)
         @checksums.include?(checksum)
       end
   
       alias :member? :include?
   
       def to_json(*a)
         result = {
           :guid => guid,
           :name => name,   # same as guid, used for id_map
           :checksums => checksums,
           :create_time => create_time,
           :is_completed => is_completed,
           :json_class => self.class.name,
           :chef_type => 'sandbox'
         }
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result.to_json(*a)
       end
   
       def self.json_create(o)
         sandbox = new(o['guid'])
         sandbox.checksums = o['checksums']
         sandbox.create_time = o['create_time']
         sandbox.is_completed = o['is_completed']
         if o.has_key?('_rev')
           sandbox.couchdb_rev = o["_rev"]
           o.delete("_rev")
         end
         if o.has_key?("_id")
           sandbox.couchdb_id = o["_id"]
           #sandbox.index_id = sandbox.couchdb_id
           o.delete("_id")
         end
         sandbox
       end
   
       ##
       # Couchdb
       ##
       
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("sandboxes", DESIGN_DOCUMENT)
       end
       
       def self.cdb_list(inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).list("sandboxes", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }            
       end
   
       def self.cdb_load(guid, couchdb=nil)
         # Probably want to look for a view here at some point
         (couchdb || Chef::CouchDB.new).load("sandbox", guid)
       end
   
       def cdb_destroy
         (couchdb || Chef::CouchDB.new).delete("sandbox", guid, @couchdb_rev)
       end
   
       def cdb_save(couchdb=nil)
         @couchdb_rev = (couchdb || Chef::CouchDB.new).store("sandbox", guid, self)["rev"]
       end
   
     end
   end

lib/chef/webui_user.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/couchdb'
   require 'chef/index_queue'
   require 'digest/sha1'
   require 'chef/json_compat'
     
   
   class Chef
     class WebUIUser
       
       attr_accessor :name, :validated, :admin, :openid, :couchdb
       attr_reader   :password, :salt, :couchdb_id, :couchdb_rev
       
       include Chef::Mixin::ParamsValidate
       
       DESIGN_DOCUMENT = {
         "version" => 3,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
               function(doc) {
                 if (doc.chef_type == "webui_user") {
                   emit(doc.name, doc);
                 }
               }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "webui_user") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           },
         },
       }
       
       # Create a new Chef::WebUIUser object.
       def initialize(opts={})
         @name, @salt, @password = opts['name'], opts['salt'], opts['password']
         @openid, @couchdb_rev, @couchdb_id = opts['openid'], opts['_rev'], opts['_id']
         @admin = false
         @couchdb = Chef::CouchDB.new
       end
       
       def name=(n)
         @name = n.gsub(/\./, '_')
       end
       
       def admin?
         admin
       end
       
       # Set the password for this object.
       def set_password(password, confirm_password=password) 
         raise ArgumentError, "Passwords do not match" unless password == confirm_password
         raise ArgumentError, "Password cannot be blank" if (password.nil? || password.length==0)
         raise ArgumentError, "Password must be a minimum of 6 characters" if password.length < 6
         generate_salt
         @password = encrypt_password(password)      
       end
       
       def set_openid(given_openid)
         @openid = given_openid
       end 
       
       def verify_password(given_password)
         encrypt_password(given_password) == @password
       end 
       
       # Serialize this object as a hash 
       def to_json(*a)
         attributes = Hash.new
         recipes = Array.new
         result = {
           'name' => name,
           'json_class' => self.class.name,
           'salt' => salt,
           'password' => password,
           'openid' => openid,
           'admin' => admin,
           'chef_type' => 'webui_user',
         }
         result["_id"]  = @couchdb_id if @couchdb_id  
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result.to_json(*a)
       end
       
       # Create a Chef::WebUIUser from JSON
       def self.json_create(o)
         me = new(o)
         me.admin = o["admin"]
         me
       end
       
       # List all the Chef::WebUIUser objects in the CouchDB.  If inflate is set to true, you will get
       # the full list of all registration objects.  Otherwise, you'll just get the IDs
       def self.cdb_list(inflate=false)
         rs = Chef::CouchDB.new.list("users", inflate)
         if inflate
           rs["rows"].collect { |r| r["value"] }
         else
           rs["rows"].collect { |r| r["key"] }
         end
       end
       
       def self.list(inflate=false)
         r = Chef::REST.new(Chef::Config[:chef_server_url])
         if inflate
           response = Hash.new
           Chef::Search::Query.new.search(:user) do |n|
             response[n.name] = n unless n.nil?
           end
           response
         else
           r.get_rest("users")
         end
       end
       
       # Load an WebUIUser by name from CouchDB
       def self.cdb_load(name)
         Chef::CouchDB.new.load("webui_user", name)
       end
       
       # Load a User by name
       def self.load(name)
         r = Chef::REST.new(Chef::Config[:chef_server_url])
         r.get_rest("users/#{name}")
       end
       
       
       # Whether or not there is an WebUIUser with this key.
       def self.has_key?(name)
         Chef::CouchDB.new.has_key?("webui_user", name)
       end
       
       # Remove this WebUIUser from the CouchDB
       def cdb_destroy
         couchdb.delete("webui_user", @name, @couchdb_rev)
       end
       
       # Remove this WebUIUser via the REST API
       def destroy
         r = Chef::REST.new(Chef::Config[:chef_server_url])
         r.delete_rest("users/#{@name}")
       end
       
       # Save this WebUIUser to the CouchDB
       def cdb_save
         results = couchdb.store("webui_user", @name, self)
         @couchdb_rev = results["rev"]
       end
       
       # Save this WebUIUser via the REST API
       def save
         r = Chef::REST.new(Chef::Config[:chef_server_url])
         begin
           r.put_rest("users/#{@name}", self)
         rescue Net::HTTPServerException => e
           if e.response.code == "404"
             r.post_rest("users", self)
           else
             raise e
           end
         end
         self
       end
       
       # Create the WebUIUser via the REST API
       def create
         r = Chef::REST.new(Chef::Config[:chef_server_url])
         r.post_rest("users", self)
         self
       end
       
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         couchdb ||= Chef::CouchDB.new
         couchdb.create_design_document("users", DESIGN_DOCUMENT)
       end
       
       #return true if an admin user exists. this is pretty expensive (O(n)), should think of a better way (nuo)
       def self.admin_exist
         users = self.cdb_list
         users.each do |u|
           user = self.cdb_load(u)
           if user.admin
             return user.name
           end
         end
         nil
       end
       
       protected
       
         def generate_salt
           @salt = Time.now.to_s
           chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
           1.upto(30) { |i| @salt << chars[rand(chars.size-1)] }
           @salt
         end
       
         def encrypt_password(password)
           Digest::SHA1.hexdigest("--#{salt}--#{password}--")
         end
       
     end
   end

lib/chef/provider/execute.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/shell_out'
   require 'chef/log'
   require 'chef/provider'
   
   class Chef
     class Provider
       class Execute < Chef::Provider
   
         include Chef::Mixin::ShellOut
   
         def load_current_resource
           true
         end
   
         def action_run
           opts = {}
   
           if sentinel_file = @new_resource.creates
             if ::File.exists?(sentinel_file)
               Chef::Log.debug("#{@new_resource} sentinel file #{sentinel_file} exists - nothing to do")
               return false
             end
           end
   
           # original implementation did not specify a timeout, but ShellOut
           # *always* times out. So, set a very long default timeout
           opts[:timeout] = @new_resource.timeout || 3600
           opts[:returns] = @new_resource.returns if @new_resource.returns
           opts[:environment] = @new_resource.environment if @new_resource.environment
           opts[:user] = @new_resource.user if @new_resource.user
           opts[:group] = @new_resource.group if @new_resource.group
           opts[:cwd] = @new_resource.cwd if @new_resource.cwd
           opts[:umask] = @new_resource.umask if @new_resource.umask
           opts[:command_log_level] = :info
           opts[:command_log_prepend] = @new_resource.to_s
           if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
             opts[:live_stream] = STDOUT
           end
   
           result = shell_out!(@new_resource.command, opts)
           @new_resource.updated_by_last_action(true)
           Chef::Log.info("#{@new_resource} ran successfully")
         end
   
       end
     end
   end

lib/chef/provider/cookbook_file.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/file_access_control'
   require 'chef/provider/file'
   require 'tempfile'
   
   class Chef
     class Provider
       class CookbookFile < Chef::Provider::File
   
         def load_current_resource
           @current_resource = Chef::Resource::CookbookFile.new(@new_resource.name)
           @new_resource.path.gsub!(/\\/, "/") # for Windows
           @current_resource.path(@new_resource.path)
           @current_resource
         end
   
         def action_create
           assert_enclosing_directory_exists!
           if file_cache_location && content_stale?
             Chef::Log.debug("#{@new_resource} has new contents")
             backup_new_resource
             Tempfile.open(::File.basename(@new_resource.name)) do |staging_file|
               Chef::Log.debug("#{@new_resource} staging #{file_cache_location} to #{staging_file.path}")
               staging_file.close
               stage_file_to_tmpdir(staging_file.path)
               FileUtils.mv(staging_file.path, @new_resource.path)
             end
             Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
             @new_resource.updated_by_last_action(true)
           else
             set_all_access_controls(@new_resource.path)
           end
           @new_resource.updated_by_last_action?
         end
   
         def action_create_if_missing
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.")
           else
             action_create
           end
         end
   
         def file_cache_location
           @file_cache_location ||= begin
             cookbook = run_context.cookbook_collection[resource_cookbook]
             cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
           end
         end
   
         # Determine the cookbook to get the file from. If new resource sets an
         # explicit cookbook, use it, otherwise fall back to the implicit cookbook
         # i.e., the cookbook the resource was declared in.
         def resource_cookbook
           @new_resource.cookbook || @new_resource.cookbook_name
         end
   
         # Copy the file from the cookbook cache to a temporary location and then
         # set its file access control settings.
         def stage_file_to_tmpdir(staging_file_location)
           FileUtils.cp(file_cache_location, staging_file_location)
           set_all_access_controls(staging_file_location)
         end
   
         def set_all_access_controls(file)
           access_controls = Chef::FileAccessControl.new(@new_resource, file)
           access_controls.set_all
           @new_resource.updated_by_last_action(access_controls.modified?)
         end
   
         def backup_new_resource
           if ::File.exists?(@new_resource.path)
             backup @new_resource.path
           end
         end
   
         def content_stale?
           ( ! ::File.exist?(@new_resource.path)) || ( ! compare_content)
         end
   
       end
     end
   end

lib/chef/resource/template.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/file'
   
   class Chef
     class Resource
       class Template < Chef::Resource::File
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :template
           @action = "create"
           @source = "#{::File.basename(name)}.erb"
           @cookbook = nil
           @local = false
           @variables = Hash.new
         end
   
         def source(file=nil)
           set_or_return(
             :source,
             file,
             :kind_of => [ String ]
           )
         end
   
         def variables(args=nil)
           set_or_return(
             :variables,
             args,
             :kind_of => [ Hash ]
           )
         end
         
         def cookbook(args=nil)
           set_or_return(
             :cookbook,
             args,
             :kind_of => [ String ]
           )
         end
   
         def local(args=nil)
           set_or_return(
             :local,
             args,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
       end
     end
   end

lib/chef/knife/core/object_loader.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Knife
       module Core
         class ObjectLoader
   
           attr_reader :ui
           attr_reader :klass
   
           def initialize(klass, ui)
             @klass = klass
             @ui = ui
           end
   
           def load_from(repo_location, *components)
             unless object_file = find_file(repo_location, *components)
               ui.error "Could not find or open file for #{components.join(' ')}"
               exit 1
             end
             object_from_file(object_file)
           end
   
           def find_file(repo_location, *components)
             if file_exists_and_is_readable?(File.expand_path( components.last ))
               File.expand_path( components.last )
             else
               relative_path = File.join(Dir.pwd, repo_location, *components)
               if file_exists_and_is_readable?(relative_path)
                 relative_path
               else
                 nil
               end
             end
           end
   
           def object_from_file(filename)
             case filename
             when /\.(js|json)$/
               Chef::JSONCompat.from_json(IO.read(filename))
             when /\.rb$/
               r = klass.new
               r.from_file(filename)
               r
             else
               ui.fatal("File must end in .js, .json, or .rb")
               exit 30
             end
           end
   
           def file_exists_and_is_readable?(file)
             File.exists?(file) && File.readable?(file)
           end
   
         end
       end
     end
   end
   

lib/chef/resource_collection/stepable_iterator.rb

   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class ResourceCollection
       class StepableIterator
         
         def self.for_collection(new_collection)
           instance = new(new_collection)
           instance
         end
         
         attr_accessor :collection
         attr_reader :position
         
         def initialize(collection=[])
           @position = 0
           @paused = false
           @collection = collection
         end
         
         def size
           collection.size
         end
         
         def each(&block)
           reset_iteration(block)
           @iterator_type = :element
           iterate
         end
         
         def each_index(&block)
           reset_iteration(block)
           @iterator_type = :index
           iterate
         end
         
         def each_with_index(&block)
           reset_iteration(block)
           @iterator_type = :element_with_index
           iterate
         end
         
         def paused?
           @paused
         end
         
         def pause
           @paused = true
         end
         
         def resume
           @paused = false
           iterate
         end
         
         def rewind
           @position = 0
         end
         
         def skip_back(skips=1)
           @position -= skips
         end
         
         def skip_forward(skips=1)
           @position += skips
         end
         
         def step
           return nil if @position == size
           call_iterator_block
           @position += 1
         end
         
         def iterate_on(iteration_type, &block)
           @iterator_type = iteration_type
           @iterator_block = block
         end
         
         private
         
         def reset_iteration(iterator_block)
           @iterator_block = iterator_block
           @position = 0
           @paused = false
         end
         
         def iterate
           while @position < size && !paused?
             step
           end
           collection
         end
         
         def call_iterator_block
           case @iterator_type
           when :element
             @iterator_block.call(collection[@position])
           when :index
             @iterator_block.call(@position)
           when :element_with_index
             @iterator_block.call(collection[@position], @position)
           else
             raise "42error: someone forgot to set @iterator_type, wtf?"
           end
         end
         
       end
     end
   end

lib/chef/index_queue/indexable.rb

   #
   # Author:: Daniel DeLeo ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'chef/json_compat'
   
   class Chef
     module IndexQueue
       module Indexable
         
         module ClassMethods
           
           def index_object_type(explicit_type_name=nil)
             @index_object_type = explicit_type_name.to_s if explicit_type_name
             @index_object_type
           end
           
           # Resets all metadata used for indexing to nil. Used for testing
           def reset_index_metadata!
             @index_object_type = nil
           end
           
         end
   
         def self.included(including_class)
           including_class.send(:extend, ClassMethods)
         end
         
         attr_accessor :index_id
         
         def index_object_type
           self.class.index_object_type || Mixin::ConvertToClassName.snake_case_basename(self.class.name)
         end
         
         def with_indexer_metadata(indexer_metadata={})
           # changing input param symbol keys to strings, as the keys in hash that goes to solr are expected to be strings [cb]
           # Ruby 1.9 hates you, cb [dan]
           with_metadata = {}
           indexer_metadata.each_key do |key|
             with_metadata[key.to_s] = indexer_metadata[key]
           end
   
           with_metadata["type"]     ||= self.index_object_type
           with_metadata["id"]       ||= self.index_id
           with_metadata["database"] ||= Chef::Config[:couchdb_database]
           with_metadata["item"]     ||= self.to_hash
           with_metadata["enqueued_at"] ||= Time.now.utc.to_i
   
           raise ArgumentError, "Type, Id, or Database missing in index operation: #{with_metadata.inspect}" if (with_metadata["id"].nil? or with_metadata["type"].nil?)
           with_metadata        
         end
   
         def add_to_index(metadata={})
          Chef::Log.debug("Pushing item to index queue for addition: #{self.with_indexer_metadata(metadata)}")
          object_with_metadata = with_indexer_metadata(metadata)
          obj_id = object_with_metadata["id"]
          obj = {:action => :add, :payload => self.with_indexer_metadata(metadata)}
   
          publish_object(obj_id, obj)
         end
   
         def delete_from_index(metadata={})
           Chef::Log.debug("Pushing item to index queue for deletion: #{self.with_indexer_metadata(metadata)}")
           object_with_metadata = with_indexer_metadata(metadata)
           obj_id = object_with_metadata["id"]
           obj = {:action => :delete, :payload => self.with_indexer_metadata(metadata)}
   
           publish_object(obj_id, obj)
         end
   
         private
   
         # Uses the publisher to update the object's queue. If
         # Chef::Config[:persistent_queue] is true, the update is wrapped
         # in a transaction.
         def publish_object(object_id, object)
           publisher = AmqpClient.instance
           begin
             publisher.amqp_client.tx_select if Chef::Config[:persistent_queue]
             publisher.queue_for_object(object_id) do |queue|
               queue.publish(Chef::JSONCompat.to_json(object), :persistent => Chef::Config[:persistent_queue])
             end
             publisher.amqp_client.tx_commit if Chef::Config[:persistent_queue]
           rescue
             publisher.amqp_client.tx_rollback if Chef::Config[:persistent_queue]
             raise
           end
   
           true
         end
   
       end
     end
   end

lib/chef/provider/group/groupadd.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Provider
       class Group
         class Groupadd < Chef::Provider::Group
           
           def required_binaries
             [ "/usr/sbin/groupadd",
               "/usr/sbin/groupmod",
               "/usr/sbin/groupdel" ]
           end
   
           def load_current_resource
             super
             required_binaries.each do |required_binary|
               raise Chef::Exceptions::Group, "Could not find binary #{required_binary} for #{@new_resource}" unless ::File.exists?(required_binary)
             end
           end
   
           # Create the group
           def create_group
             command = "groupadd"
             command << set_options
             command << groupadd_options
             run_command(:command => command)
             modify_group_members    
           end
           
           # Manage the group when it already exists
           def manage_group
             command = "groupmod"
             command << set_options
             run_command(:command => command)
             modify_group_members
           end
           
           # Remove the group
           def remove_group
             run_command(:command => "groupdel #{@new_resource.group_name}")
           end
           
           def modify_group_members
             raise Chef::Exceptions::Group, "you must override modify_group_members in #{self.to_s}"
           end
           # Little bit of magic as per Adam's useradd provider to pull the assign the command line flags
           #
           # ==== Returns
           # :: A string containing the option and then the quoted value
           def set_options
             opts = ""
             { :gid => "-g" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option|
               if @current_resource.send(field) != @new_resource.send(field)
                 if @new_resource.send(field)
                   opts << " #{option} '#{@new_resource.send(field)}'"
                   Chef::Log.debug("#{@new_resource} set #{field.to_s} to #{@new_resource.send(field)}")
                 end
               end
             end
             opts << " #{@new_resource.group_name}"
           end
   
           def groupadd_options
             opts = ''
             case node[:platform]
             when "centos", "redhat", "scientific", "fedora"
               opts << " -r" if @new_resource.system
             end
             opts
           end
   
         end
       end
     end
   end

lib/chef/knife/core/bootstrap_context.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/run_list'
   class Chef
     class Knife
       module Core
         # Instances of BootstrapContext are the context objects (i.e., +self+) for
         # bootstrap templates. For backwards compatability, they +must+ set the
         # following instance variables:
         # * @config   - a hash of knife's config values
         # * @run_list - the run list for the node to boostrap
         # 
         class BootstrapContext
   
           def initialize(config, run_list, chef_config)
             @config       = config
             @run_list     = run_list
             @chef_config  = chef_config
           end
   
           def bootstrap_version_string
             if @config[:prerelease]
               "--prerelease"
             else
               "--version #{chef_version}"
             end
           end
   
           def bootstrap_environment
             @chef_config[:environment] || '_default'
           end
   
           def validation_key
             IO.read(@chef_config[:validation_key])
           end
   
           def encrypted_data_bag_secret
             IO.read(@chef_config[:encrypted_data_bag_secret])
           end
   
           def config_content
             client_rb = <<-CONFIG
   log_level        :info
   log_location     STDOUT
   chef_server_url  "#{@chef_config[:chef_server_url]}"
   validation_client_name "#{@chef_config[:validation_client_name]}"
   CONFIG
             if @config[:chef_node_name]
               client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
             else
               client_rb << "# Using default node name (fqdn)\n"
             end
   
             if knife_config[:bootstrap_proxy]
               client_rb << %Q{http_proxy        "#{knife_config[:bootstrap_proxy]}"\n}
               client_rb << %Q{https_proxy       "#{knife_config[:bootstrap_proxy]}"\n}
             end
   
             if @chef_config[:encrypted_data_bag_secret]
               client_rb << %Q{encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"\n}
             end
   
             client_rb
           end
   
           def start_chef
             s = "/usr/bin/chef-client -j /etc/chef/first-boot.json"
             s << " -E #{bootstrap_environment}" if chef_version.to_f != 0.9 # only use the -E option on Chef 0.10+
             s
           end
   
           def knife_config
             @chef_config.key?(:knife) ? @chef_config[:knife] : {}
           end
   
           def chef_version
             knife_config[:bootstrap_version] || Chef::VERSION
           end
   
         end
       end
     end
   end
   

lib/chef/provider/template.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/file'
   require 'chef/mixin/template'
   require 'chef/mixin/checksum'
   require 'chef/file_access_control'
   
   class Chef
     class Provider
   
       class Template < Chef::Provider::File
   
         include Chef::Mixin::Checksum
         include Chef::Mixin::Template
   
         def load_current_resource
           super
           @current_resource.checksum(checksum(@current_resource.path)) if ::File.exist?(@current_resource.path)
         end
   
         def action_create
           render_with_context(template_location) do |rendered_template|
             rendered(rendered_template)
             if ::File.exist?(@new_resource.path) && content_matches?
               Chef::Log.debug("#{@new_resource} content has not changed.")
               set_all_access_controls(@new_resource.path)
             else
               backup
               set_all_access_controls(rendered_template.path)
               FileUtils.mv(rendered_template.path, @new_resource.path)
               Chef::Log.info("#{@new_resource} updated content")
               @new_resource.updated_by_last_action(true)
             end
           end
         end
   
         def action_create_if_missing
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug("#{@new_resource} exists - taking no action")
           else
             action_create
           end
         end
   
         def template_location
           @template_file_cache_location ||= begin
             if @new_resource.local
               @new_resource.source
             else
               cookbook = run_context.cookbook_collection[resource_cookbook]
               cookbook.preferred_filename_on_disk_location(node, :templates, @new_resource.source)
             end
           end
         end
         
         def resource_cookbook
           @new_resource.cookbook || @new_resource.cookbook_name
         end
   
         def rendered(rendered_template)
           @new_resource.checksum(checksum(rendered_template.path))
           Chef::Log.debug("Current content's checksum:  #{@current_resource.checksum}")
           Chef::Log.debug("Rendered content's checksum: #{@new_resource.checksum}")
         end
   
         def content_matches?
           @current_resource.checksum == @new_resource.checksum
         end
   
         def set_all_access_controls(file)
           access_controls = Chef::FileAccessControl.new(@new_resource, file)
           access_controls.set_all
           @new_resource.updated_by_last_action(access_controls.modified?)
         end
   
         private
   
         def render_with_context(template_location, &block)
           context = {}
           context.merge!(@new_resource.variables)
           context[:node] = node
           render_template(IO.read(template_location), context, &block)
         end
   
       end
     end
   end

lib/chef/provider/file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/log'
   require 'chef/resource/file'
   require 'chef/mixin/checksum'
   require 'chef/provider'
   require 'etc'
   require 'fileutils'
   
   class Chef
     class Provider
       class File < Chef::Provider
         include Chef::Mixin::Checksum
   
         def negative_complement(big)
           if big > 1073741823 # Fixnum max
             big -= (2**32) # diminished radix wrap to negative
           end
           big
         end
   
         def octal_mode(mode)
           ((mode.respond_to?(:oct) ? mode.oct : mode.to_i) & 007777)
         end
   
         private :negative_complement, :octal_mode
   
         def load_current_resource
           @current_resource = Chef::Resource::File.new(@new_resource.name)
           @new_resource.path.gsub!(/\\/, "/") # for Windows
           @current_resource.path(@new_resource.path)
           if ::File.exist?(@current_resource.path) && ::File.readable?(@current_resource.path)
             cstats = ::File.stat(@current_resource.path)
             @current_resource.owner(cstats.uid)
             @current_resource.group(cstats.gid)
             @current_resource.mode(octal_mode(cstats.mode))
           end
           @current_resource
         end
   
         # Compare the content of a file.  Returns true if they are the same, false if they are not.
         def compare_content
           checksum(@current_resource.path) == new_resource_content_checksum
         end
   
         # Set the content of the file, assuming it is not set correctly already.
         def set_content
           unless compare_content
             backup @new_resource.path if ::File.exists?(@new_resource.path)
             ::File.open(@new_resource.path, "w") {|f| f.write @new_resource.content }
             Chef::Log.info("#{@new_resource} contents updated")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         # Compare the ownership of a file.  Returns true if they are the same, false if they are not.
         def compare_owner
           return false if @new_resource.owner.nil?
   
           @set_user_id = case @new_resource.owner
           when /^\d+$/, Integer
             @new_resource.owner.to_i
           else
             # This raises an ArgumentError if you can't find the user
             Etc.getpwnam(@new_resource.owner).uid
           end
   
           @set_user_id == @current_resource.owner
         end
   
         # Set the ownership on the file, assuming it is not set correctly already.
         def set_owner
           unless compare_owner
             @set_user_id = negative_complement(@set_user_id)
             ::File.chown(@set_user_id, nil, @new_resource.path)
             Chef::Log.info("#{@new_resource} owner changed to #{@set_user_id}")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         # Compares the group of a file.  Returns true if they are the same, false if they are not.
         def compare_group
           return false if @new_resource.group.nil?
   
           @set_group_id = case @new_resource.group
           when /^\d+$/, Integer
             @new_resource.group.to_i
           else
             Etc.getgrnam(@new_resource.group).gid
           end
   
           @set_group_id == @current_resource.group
         end
   
         def set_group
           unless compare_group
             @set_group_id = negative_complement(@set_group_id)
             ::File.chown(nil, @set_group_id, @new_resource.path)
             Chef::Log.info("#{@new_resource} group changed to #{@set_group_id}")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def compare_mode
           case @new_resource.mode
           when /^\d+$/, Integer
             octal_mode(@new_resource.mode) == octal_mode(@current_resource.mode)
           else
             false
           end
         end
   
         def set_mode
           unless compare_mode && @new_resource.mode != nil
             # CHEF-174, bad mojo around treating integers as octal.  If a string is passed, we try to do the "right" thing
             ::File.chmod(octal_mode(@new_resource.mode), @new_resource.path)
             Chef::Log.info("#{@new_resource} mode changed to #{sprintf("%o" % octal_mode(@new_resource.mode))}")
             @new_resource.updated_by_last_action(true)
           end
         end
   
         def action_create
           assert_enclosing_directory_exists!
           unless ::File.exists?(@new_resource.path)
             ::File.open(@new_resource.path, "w+") {|f| f.write @new_resource.content }
             @new_resource.updated_by_last_action(true)
             Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
           else
             set_content unless @new_resource.content.nil?
           end
           set_owner unless @new_resource.owner.nil?
           set_group unless @new_resource.group.nil?
           set_mode unless @new_resource.mode.nil?
         end
   
         def action_create_if_missing
           if ::File.exists?(@new_resource.path)
             Chef::Log.debug("File #{@new_resource.path} exists, taking no action.")
           else
             action_create
           end
         end
   
         def action_delete
           if ::File.exists?(@new_resource.path)
             if ::File.writable?(@new_resource.path)
               backup unless ::File.symlink?(@new_resource.path)
               ::File.delete(@new_resource.path)
               Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}")
               @new_resource.updated_by_last_action(true)
             else
               raise "Cannot delete #{@new_resource} at #{@new_resource_path}!"
             end
           end
         end
   
         def action_touch
           action_create
           time = Time.now
           ::File.utime(time, time, @new_resource.path)
           Chef::Log.info("#{@new_resource} updated atime and mtime to #{time}")
           @new_resource.updated_by_last_action(true)
         end
   
         def backup(file=nil)
           file ||= @new_resource.path
           if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(file)
             time = Time.now
             savetime = time.strftime("%Y%m%d%H%M%S")
             backup_filename = "#{@new_resource.path}.chef-#{savetime}"
             backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows
             # if :file_backup_path is nil, we fallback to the old behavior of
             # keeping the backup in the same directory. We also need to to_s it
             # so we don't get a type error around implicit to_str conversions.
             prefix = Chef::Config[:file_backup_path].to_s
             backup_path = ::File.join(prefix, backup_filename)
             FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path]
             FileUtils.cp(file, backup_path, :preserve => true)
             Chef::Log.info("#{@new_resource} backed up to #{backup_path}")
   
             # Clean up after the number of backups
             slice_number = @new_resource.backup
             backup_files = Dir[::File.join(prefix, ".#{@new_resource.path}.chef-*")].sort { |a,b| b <=> a }
             if backup_files.length >= @new_resource.backup
               remainder = backup_files.slice(slice_number..-1)
               remainder.each do |backup_to_delete|
                 FileUtils.rm(backup_to_delete)
                 Chef::Log.info("#{@new_resource} removed backup at #{backup_to_delete}")
               end
             end
           end
         end
   
         private
   
         def assert_enclosing_directory_exists!
           enclosing_dir = ::File.dirname(@new_resource.path)
           unless ::File.directory?(enclosing_dir)
             msg = "Cannot create a file at #{@new_resource.path} because the enclosing directory (#{enclosing_dir}) does not exist"
             raise Chef::Exceptions::EnclosingDirectoryDoesNotExist, msg
           end
         end
   
         def new_resource_content_checksum
           @new_resource.content && Digest::SHA2.hexdigest(@new_resource.content)
         end
       end
     end
   end

lib/chef/provider/group/usermod.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/group/groupadd'
   
   class Chef
     class Provider
       class Group
         class Usermod < Chef::Provider::Group::Groupadd
           
           def load_current_resource
             super
   
             raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/usermod for #{@new_resource}" unless ::File.exists?("/usr/sbin/usermod")
           end
   
           def modify_group_members
             case node[:platform]
             when "openbsd", "netbsd", "aix"
               append_flags = "-G"
             when "solaris"
               append_flags = "-a -G"
             end
   
             unless @new_resource.members.empty?
               if(@new_resource.append)
                 @new_resource.members.each do |member|
                   Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}")
                   run_command(:command => "usermod #{append_flags} #{@new_resource.group_name} #{member}" )
   
                 end
               else
                 raise Chef::Exceptions::Group, "setting group members directly is not supported by #{self.to_s}"
               end
             else
               Chef::Log.debug("#{@new_resource} not changing group members, the group has no members")
             end
           end
         end
       end
     end
   end

lib/chef/run_context.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2008-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/resource_collection'
   require 'chef/node'
   require 'chef/role'
   require 'chef/log'
   require 'chef/mixin/language_include_recipe'
   
   class Chef
     # == Chef::RunContext
     # Value object that loads and tracks the context of a Chef run
     class RunContext
   
       # Used to load the node's recipes after expanding its run list
       include Chef::Mixin::LanguageIncludeRecipe
   
       attr_reader :node, :cookbook_collection, :definitions
   
       # Needs to be settable so deploy can run a resource_collection independent
       # of any cookbooks.
       attr_accessor :resource_collection
   
       # Creates a new Chef::RunContext object and populates its fields. This object gets
       # used by the Chef Server to generate a fully compiled recipe list for a node.
       #
       # === Returns
       # object:: Duh. :)
       def initialize(node, cookbook_collection)
         @node = node
         @cookbook_collection = cookbook_collection
         @resource_collection = Chef::ResourceCollection.new
         @definitions = Hash.new
   
         # TODO: 5/18/2010 cw/timh - See note on Chef::Node's
         # cookbook_collection attr_accessor
         node.cookbook_collection = cookbook_collection
       end
   
       def load(run_list_expansion)
         load_libraries
         load_lwrp_providers
         load_lwrp_resources
         load_attributes
         load_resource_definitions
   
         # Precendence rules state that roles' attributes come after
         # cookbooks. Now we've loaded attributes from cookbooks with
         # load_attributes, apply the expansion attributes (loaded from
         # roles) to the node.
         @node.apply_expansion_attributes(run_list_expansion)
   
         run_list_expansion.recipes.each do |recipe|
           # TODO: timh/cw, 5-14-2010: It's distasteful to be including
           # the DSL in a class outside the context of the DSL
           include_recipe(recipe)
         end
       end
   
   
       private
   
       def load_libraries
         foreach_cookbook_load_segment(:libraries) do |cookbook_name, filename|
           Chef::Log.debug("Loading cookbook #{cookbook_name}'s library file: #{filename}")
           Kernel.load(filename)
         end
       end
   
       def load_lwrp_providers
         foreach_cookbook_load_segment(:providers) do |cookbook_name, filename|
           Chef::Log.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}")
           Chef::Provider.build_from_file(cookbook_name, filename, self)
         end
       end
   
       def load_lwrp_resources
         foreach_cookbook_load_segment(:resources) do |cookbook_name, filename|
           Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
           Chef::Resource.build_from_file(cookbook_name, filename, self)
         end
       end
   
       def load_attributes
         node.load_attributes
       end
   
       def load_resource_definitions
         foreach_cookbook_load_segment(:definitions) do |cookbook_name, filename|
           Chef::Log.debug("Loading cookbook #{cookbook_name}'s definitions from #{filename}")
           resourcelist = Chef::ResourceDefinitionList.new
           resourcelist.from_file(filename)
           definitions.merge!(resourcelist.defines) do |key, oldval, newval|
             Chef::Log.info("Overriding duplicate definition #{key}, new definition found in #{filename}")
             newval
           end
         end
       end
   
       def foreach_cookbook_load_segment(segment, &block)
         cookbook_collection.each do |cookbook_name, cookbook|
           segment_filenames = cookbook.segment_filenames(segment)
           segment_filenames.each do |segment_filename|
             block.call(cookbook_name, segment_filename)
           end
         end
       end
   
     end
   end

lib/chef/provider/package/rubygems.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/package'
   require 'chef/mixin/command'
   require 'chef/resource/package'
   require 'chef/mixin/get_source_from_package'
   
   # Class methods on Gem are defined in rubygems
   require 'rubygems'
   # Ruby 1.9's gem_prelude can interact poorly with loading the full rubygems
   # explicitly like this. Make sure rubygems/specification is always last in this
   # list
   require 'rubygems/version'
   require 'rubygems/dependency'
   require 'rubygems/spec_fetcher'
   require 'rubygems/platform'
   require 'rubygems/format'
   require 'rubygems/dependency_installer'
   require 'rubygems/uninstaller'
   require 'rubygems/specification'
   
   class Chef
     class Provider
       class Package
         class Rubygems < Chef::Provider::Package
           class GemEnvironment
             # HACK: trigger gem config load early. Otherwise it can get lazy
             # loaded during operations where we've set Gem.sources to an
             # alternate value and overwrite it with the defaults.
             Gem.configuration
   
             DEFAULT_UNINSTALLER_OPTS = {:ignore => true, :executables => true}
   
             ##
             # The paths where rubygems should search for installed gems.
             # Implemented by subclasses.
             def gem_paths
               raise NotImplementedError
             end
   
             ##
             # A rubygems source index containing the list of gemspecs for all
             # available gems in the gem installation.
             # Implemented by subclasses
             # === Returns
             # Gem::SourceIndex
             def gem_source_index
               raise NotImplementedError
             end
   
             ##
             # Lists the installed versions of +gem_name+, constrained by the
             # version spec in +gem_dep+
             # === Arguments
             # Gem::Dependency   +gem_dep+ is a Gem::Dependency object, its version
             #                   specification constrains which gems are returned.
             # === Returns
             # [Gem::Specification]  an array of Gem::Specification objects
             def installed_versions(gem_dep)
               gem_source_index.search(gem_dep)
             end
   
             ##
             # Yields to the provided block with rubygems' source list set to the
             # list provided. Always resets the list when the block returns or
             # raises an exception.
             def with_gem_sources(*sources)
               sources.compact!
               original_sources = Gem.sources
               Gem.sources = sources unless sources.empty?
               yield
             ensure
               Gem.sources = original_sources
             end
   
             ##
             # Determines the candidate version for a gem from a .gem file on disk
             # and checks if it matches the version contraints in +gem_dependency+
             # === Returns
             # Gem::Version  a singular gem version object is returned if the gem
             #               is available
             # nil           returns nil if the gem on disk doesn't match the
             #               version constraints for +gem_dependency+
             def candidate_version_from_file(gem_dependency, source)
               spec = Gem::Format.from_file_by_path(source).spec
               if spec.satisfies_requirement?(gem_dependency)
                 logger.debug {"#{@new_resource} found candidate gem version #{spec.version} from local gem package #{source}"}
                 spec.version
               else
                 # This is probably going to end badly...
                 logger.warn { "#{@new_resource} gem package #{source} does not satisfy the requirements #{gem_dependency.to_s}" }
                 nil
               end
             end
   
             ##
             # Finds the newest version that satisfies the constraints of
             # +gem_dependency+. The version is determined from the cache or a
             # round-trip to the server as needed. The architecture and gem
             # sources will be set before making the query.
             # === Returns
             # Gem::Version  a singular gem version object is returned if the gem
             #               is available
             # nil           returns nil if the gem could not be found
             def candidate_version_from_remote(gem_dependency, *sources)
               raise NotImplementedError
             end
   
             ##
             # Find the newest gem version available from Gem.sources that satisfies
             # the constraints of +gem_dependency+
             def find_newest_remote_version(gem_dependency, *sources)
               # DependencyInstaller sorts the results such that the last one is
               # always the one it considers best.
               spec_with_source = dependency_installer.find_gems_with_sources(gem_dependency).last
   
               spec = spec_with_source && spec_with_source[0]
               version = spec && spec_with_source[0].version
               if version
                 logger.debug { "#{@new_resource} found gem #{spec.name} version #{version} for platform #{spec.platform} from #{spec_with_source[1]}" }
                 version
               else
                 source_list = sources.compact.empty? ? "[#{Gem.sources.join(', ')}]" : "[#{sources.join(', ')}]"
                 logger.warn { "#{@new_resource} failed to find gem #{gem_dependency} from #{source_list}" }
                 nil
               end
             end
   
             ##
             # Installs a gem via the rubygems ruby API.
             # === Options
             # :sources    rubygems servers to use
             # Other options are passed to Gem::DependencyInstaller.new
             def install(gem_dependency, options={})
               with_gem_sources(*options.delete(:sources)) do
                 with_correct_verbosity do
                   dependency_installer(options).install(gem_dependency)
                 end
               end
             end
   
             ##
             # Uninstall the gem +gem_name+ via the rubygems ruby API. If
             # +gem_version+ is provided, only that version will be uninstalled.
             # Otherwise, all versions are uninstalled.
             # === Options
             # Options are passed to Gem::Uninstaller.new
             def uninstall(gem_name, gem_version=nil, opts={})
               gem_version ? opts[:version] = gem_version : opts[:all] = true
               with_correct_verbosity do
                 uninstaller(gem_name, opts).uninstall
               end
             end
   
             ##
             # Set rubygems' user interaction to ConsoleUI or SilentUI depending
             # on our current debug level
             def with_correct_verbosity
               Gem::DefaultUserInteraction.ui = Chef::Log.debug? ? Gem::ConsoleUI.new : Gem::SilentUI.new
               yield
             end
   
             def dependency_installer(opts={})
               Gem::DependencyInstaller.new(opts)
             end
   
             def uninstaller(gem_name, opts={})
               Gem::Uninstaller.new(gem_name, DEFAULT_UNINSTALLER_OPTS.merge(opts))
             end
   
             private
   
             def logger
               Chef::Log.logger
             end
   
           end
   
           class CurrentGemEnvironment < GemEnvironment
   
             def gem_paths
               Gem.path
             end
   
             def gem_source_index
               Gem.source_index
             end
   
             def candidate_version_from_remote(gem_dependency, *sources)
               with_gem_sources(*sources) do
                 find_newest_remote_version(gem_dependency, *sources)
               end
             end
   
           end
   
           class AlternateGemEnvironment < GemEnvironment
             JRUBY_PLATFORM = /(:?universal|x86_64|x86)\-java\-[0-9\.]+/
             
             def self.gempath_cache
               @gempath_cache ||= {}
             end
   
             def self.platform_cache
               @platform_cache ||= {}
             end
   
             include Chef::Mixin::ShellOut
   
             attr_reader :gem_binary_location
   
             def initialize(gem_binary_location)
               @gem_binary_location = gem_binary_location
             end
   
             def gem_paths
               if self.class.gempath_cache.key?(@gem_binary_location)
                 self.class.gempath_cache[@gem_binary_location]
               else
                 # shellout! is a fork/exec which won't work on windows
                 shell_style_paths = shell_out!("#{@gem_binary_location} env gempath").stdout
                 # on windows, the path separator is (usually? always?) semicolon
                 paths = shell_style_paths.split(::File::PATH_SEPARATOR).map { |path| path.strip }
                 self.class.gempath_cache[@gem_binary_location] = paths
               end
             end
   
             def gem_source_index
               @source_index ||= Gem::SourceIndex.from_gems_in(*gem_paths.map { |p| p + '/specifications' })
             end
   
             ##
             # Attempt to detect the correct platform settings for the target gem
             # environment.
             #
             # In practice, this only makes a difference if different versions are
             # available depending on platform, and only if the target gem
             # environment has a radically different platform (i.e., jruby), so we
             # just try to detect jruby and fall back to the current platforms
             # (Gem.platforms) if we don't detect it.
             #
             # === Returns
             # [String|Gem::Platform] returns an array of Gem::Platform-compatible
             # objects, i.e., Strings that are valid for Gem::Platform or actual
             # Gem::Platform objects.
             def gem_platforms
               if self.class.platform_cache.key?(@gem_binary_location)
                 self.class.platform_cache[@gem_binary_location]
               else
                 gem_environment = shell_out!("#{@gem_binary_location} env").stdout
                 if jruby = gem_environment[JRUBY_PLATFORM]
                   self.class.platform_cache[@gem_binary_location] = ['ruby', Gem::Platform.new(jruby)]
                 else
                   self.class.platform_cache[@gem_binary_location] = Gem.platforms
                 end
               end
             end
   
             def with_gem_platforms(*alt_gem_platforms)
               alt_gem_platforms.flatten!
               original_gem_platforms = Gem.platforms
               Gem.platforms = alt_gem_platforms
               yield
             ensure
               Gem.platforms = original_gem_platforms
             end
   
             def candidate_version_from_remote(gem_dependency, *sources)
               with_gem_sources(*sources) do
                 with_gem_platforms(*gem_platforms) do
                   find_newest_remote_version(gem_dependency, *sources)
                 end
               end
             end
   
           end
   
           include Chef::Mixin::ShellOut
   
           attr_reader :gem_env
   
           def logger
             Chef::Log.logger
           end
   
           include Chef::Mixin::GetSourceFromPackage
   
           def initialize(new_resource, run_context=nil)
             super
             if new_resource.gem_binary
               if new_resource.options && new_resource.options.kind_of?(Hash)
                 msg =  "options cannot be given as a hash when using an explicit gem_binary\n"
                 msg << "in #{new_resource} from #{new_resource.source_line}"
                 raise ArgumentError, msg
               end
               @gem_env = AlternateGemEnvironment.new(new_resource.gem_binary)
             else
               @gem_env = CurrentGemEnvironment.new
             end
           end
   
           def gem_dependency
             Gem::Dependency.new(@new_resource.package_name, @new_resource.version)
           end
   
           def source_is_remote?
             return true if @new_resource.source.nil?
             URI.parse(@new_resource.source).absolute?
           end
   
           def current_version
             #raise 'todo'
             # If one or more matching versions are installed, the newest of them
             # is the current version
             if !matching_installed_versions.empty?
               gemspec = matching_installed_versions.last
               logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}"}
               gemspec
             # If no version matching the requirements exists, the latest installed
             # version is the current version.
             elsif !all_installed_versions.empty?
               gemspec = all_installed_versions.last
               logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
               gemspec
             else
               logger.debug { "#{@new_resource} no installed version found for #{gem_dependency.to_s}"}
               nil
             end
           end
   
           def matching_installed_versions
             @matching_installed_versions ||= @gem_env.installed_versions(gem_dependency)
           end
   
           def all_installed_versions
             @all_installed_versions ||= begin
               @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, '>= 0'))
             end
           end
   
           def gem_sources
             @new_resource.source ? Array(@new_resource.source) : nil
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::Package::GemPackage.new(@new_resource.name)
             @current_resource.package_name(@new_resource.package_name)
             if current_spec = current_version
               @current_resource.version(current_spec.version.to_s)
             end
             @current_resource
           end
   
           def candidate_version
             @candidate_version ||= begin
               if target_version_already_installed?
                 nil
               elsif source_is_remote?
                 @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
               else
                 @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s
               end
             end
           end
   
           def target_version_already_installed?
             return false unless @current_resource && @current_resource.version
             return false if @current_resource.version.nil?
             # in the future we could support squiggly requirements like "~> 1.2.0"
             # for now, the behavior when using anything other than exact 
             # requirements is undefined.
             Gem::Requirement.new(@new_resource.version).satisfied_by?(Gem::Version.new(@current_resource.version))
           end
   
           ##
           # Installs the gem, using either the gems API or shelling out to `gem`
           # according to the following criteria:
           # 1. Use gems API (Gem::DependencyInstaller) by default
           # 2. shell out to `gem install` when a String of options is given
           # 3. use gems API with options if a hash of options is given
           def install_package(name, version)
             if source_is_remote? && @new_resource.gem_binary.nil?
               if @new_resource.options.nil?
                 @gem_env.install(gem_dependency, :sources => gem_sources)
               elsif @new_resource.options.kind_of?(Hash)
                 options = @new_resource.options
                 options[:sources] = gem_sources
                 @gem_env.install(gem_dependency, options)
               else
                 install_via_gem_command(name, version)
               end
             elsif @new_resource.gem_binary.nil?
               @gem_env.install(@new_resource.source)
             else
               install_via_gem_command(name,version)
             end
             true
           end
   
           def gem_binary_path
             @new_resource.gem_binary || 'gem'
           end
   
           def install_via_gem_command(name, version)
             src = @new_resource.source && "  --source=#{@new_resource.source} --source=http://rubygems.org"
             shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env=>nil)
           end
   
           def upgrade_package(name, version)
             install_package(name, version)
           end
   
           def remove_package(name, version)
             if @new_resource.gem_binary.nil?
               if @new_resource.options.nil?
                 @gem_env.uninstall(name, version)
               elsif @new_resource.options.kind_of?(Hash)
                 @gem_env.uninstall(name, version, @new_resource.options)
               else
                 uninstall_via_gem_command(name, version)
               end
             else
               uninstall_via_gem_command(name, version)
             end
           end
   
           def uninstall_via_gem_command(name, version)
             if version
               shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env=>nil)
             else
               shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env=>nil)
             end
           end
   
           def purge_package(name, version)
             remove_package(name, version)
           end
   
           private
   
           def opts
             expand_options(@new_resource.options)
           end
   
         end
       end
     end
   end

lib/chef/provider/service/solaris.rb

   #
   # Author:: Toomas Pelberg ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Solaris < Chef::Provider::Service
   
           def initialize(new_resource, run_context=nil)
             super
             @init_command = "/usr/sbin/svcadm"
             @status_command = "/bin/svcs -l"
           end
   
           def load_current_resource
             @current_resource = Chef::Resource::Service.new(@new_resource.name)
             @current_resource.service_name(@new_resource.service_name)
             unless ::File.exists? "/bin/svcs"
               raise Chef::Exceptions::Service, "/bin/svcs does not exist!"
             end
             @status = service_status.enabled
             @current_resource
           end
   
           def enable_service
             run_command(:command => "#{@init_command} enable #{@new_resource.service_name}")
             return service_status.enabled
           end
   
           def disable_service
             run_command(:command => "#{@init_command} disable #{@new_resource.service_name}")
             return service_status.enabled
           end
   
           alias_method :stop_service, :disable_service
           alias_method :start_service, :enable_service
   
           def reload_service
             run_command(:command => "#{@init_command} refresh #{@new_resource.service_name}")
           end
   
           def restart_service
             disable_service
             return enable_service
           end
   
           def service_status
             status = popen4("#{@status_command} #{@current_resource.service_name}") do |pid, stdin, stdout, stderr|
               stdout.each do |line|
                 case line
                 when /state\s+online/
                   @current_resource.enabled(true)
                   @current_resource.running(true)
                 end
               end
             end
             unless @current_resource.enabled
               @current_resource.enabled(false)
               @current_resource.running(false)
             end
             @current_resource
           end
   
         end
       end
     end
   end

lib/chef/knife/role_bulk_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleBulkDelete < Knife
   
         deps do
           require 'chef/role'
           require 'chef/json_compat'
         end
   
         banner "knife role bulk delete REGEX (options)"
   
         def run
           if @name_args.length < 1
             ui.error("You must supply a regular expression to match the results against")
             exit 1
           end
   
           all_roles = Chef::Role.list(true)
   
           matcher = /#{@name_args[0]}/
           roles_to_delete = {}
           all_roles.each do |name, role|
             next unless name =~ matcher
             roles_to_delete[role.name] = role
           end
   
           if roles_to_delete.empty?
             ui.info "No roles match the expression /#{@name_args[0]}/"
             exit 0
           end
   
           ui.msg("The following roles will be deleted:")
           ui.msg("")
           ui.msg(ui.list(roles_to_delete.keys.sort, :columns_down))
           ui.msg("")
           ui.confirm("Are you sure you want to delete these roles")
   
           roles_to_delete.sort.each do |name, role|
             role.destroy
             ui.msg("Deleted role #{name}")
           end
         end
       end
     end
   end
   
   
   
   
   

lib/chef/provider/group/aix.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/group/usermod'
   
   class Chef
     class Provider
       class Group
         class Aix < Chef::Provider::Group::Usermod
   
           def required_binaries
             [ "/usr/bin/mkgroup",
               "/usr/bin/chgroup",
               "/usr/sbin/rmgroup" ]
           end
   
           def create_group
             command = "mkgroup"
             command << set_options << " #{@new_resource.group_name}"
             run_command(:command => command)
             modify_group_members
           end
   
           def manage_group
             command = "chgroup"
             options = set_options
             #Usage: chgroup [-R load_module] "attr=value" ... group
             if options.size > 0
               command << options << " #{@new_resource.group_name}"
               run_command(:command => command)
             end
             modify_group_members
           end
   
           def remove_group
             run_command(:command => "rmgroup #{@new_resource.group_name}")
           end
   
           def set_options
             opts = ""
             { :gid => "id" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option|
               if @current_resource.send(field) != @new_resource.send(field)
                 if @new_resource.send(field)
                   Chef::Log.debug("#{@new_resource} setting #{field.to_s} to #{@new_resource.send(field)}")
                   opts << " '#{option}=#{@new_resource.send(field)}'"
                 end
               end
             end
             opts
           end
   
         end
       end
     end
   end

lib/chef/knife/core/node_presenter.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife/core/text_formatter'
   require 'chef/knife/core/generic_presenter'
   
   class Chef
     class Knife
       module Core
   
         # This module may be included into a knife subcommand class to automatically
         # add configuration options used by the NodePresenter
         module NodeFormattingOptions
           # :nodoc:
           # Would prefer to do this in a rational way, but can't be done b/c of
           # Mixlib::CLI's design :(
           def self.included(includer)
             includer.class_eval do
               option :medium_output,
                 :short   => '-m',
                 :long    => '--medium',
                 :boolean => true,
                 :default => false,
                 :description => 'Include normal attributes in the output'
   
               option :long_output,
                 :short   => '-l',
                 :long    => '--long',
                 :boolean => true,
                 :default => false,
                 :description => 'Include all attributes in the output'
             end
           end
         end
   
         #==Chef::Knife::Core::NodePresenter
         # A customized presenter for Chef::Node objects. Supports variable-length
         # output formats for displaying node data
         class NodePresenter < GenericPresenter
   
           def format(data)
             if parse_format_option == :json
               summarize_json(data)
             else
               super
             end
           end
   
           def summarize_json(data)
             if data.kind_of?(Chef::Node)
               node = data
               result = {}
   
               result["name"] = node.name
               result["chef_environment"] = node.chef_environment
               result["run_list"] = node.run_list
               result["normal"] = node.normal_attrs
   
               if config[:long_output]
                 result["default"]   = node.default_attrs
                 result["override"]  = node.override_attrs
                 result["automatic"] = node.automatic_attrs
               end
   
               Chef::JSONCompat.to_json_pretty(result)
             else
               Chef::JSONCompat.to_json_pretty(data)
             end
           end
   
           # Converts a Chef::Node object to a string suitable for output to a
           # terminal. If config[:medium_output] or config[:long_output] are set
           # the volume of output is adjusted accordingly. Uses colors if enabled
           # in the the ui object.
           def summarize(data)
             if data.kind_of?(Chef::Node)
               node = data
               # special case ec2 with their split horizon whatsis.
               ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress]
   
               summarized=<<-SUMMARY
   #{ui.color('Node Name:', :bold)}   #{ui.color(node.name, :bold)}
   #{key('Environment:')} #{node.chef_environment}
   #{key('FQDN:')}        #{node[:fqdn]}
   #{key('IP:')}          #{ip}
   #{key('Run List:')}    #{node.run_list}
   #{key('Roles:')}       #{Array(node[:roles]).join(', ')}
   #{key('Recipes:')}     #{Array(node[:recipes]).join(', ')}
   #{key('Platform:')}    #{node[:platform]} #{node[:platform_version]}
   SUMMARY
               if config[:medium_output] || config[:long_output]
                 summarized +=<<-MORE
   #{key('Attributes:')}
   #{text_format(node.normal_attrs)}
   MORE
               end
               if config[:long_output]
                 summarized +=<<-MOST
   #{key('Default Attributes:')}
   #{text_format(node.default_attrs)}
   #{key('Override Attributes:')}
   #{text_format(node.override_attrs)}
   #{key('Automatic Attributes (Ohai Data):')}
   #{text_format(node.automatic_attrs)}
   MOST
               end
               summarized
             else
               super
             end
           end
   
           def key(key_text)
             ui.color(key_text, :cyan)
           end
   
         end
       end
     end
   end
   

lib/chef/client.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Author:: Christopher Brown ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2008-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/log'
   require 'chef/rest'
   require 'chef/api_client'
   require 'chef/platform'
   require 'chef/node'
   require 'chef/role'
   require 'chef/file_cache'
   require 'chef/run_context'
   require 'chef/runner'
   require 'chef/run_status'
   require 'chef/cookbook/cookbook_collection'
   require 'chef/cookbook/file_vendor'
   require 'chef/cookbook/file_system_file_vendor'
   require 'chef/cookbook/remote_file_vendor'
   require 'chef/version'
   require 'ohai'
   
   class Chef
     # == Chef::Client
     # The main object in a Chef run. Preps a Chef::Node and Chef::RunContext,
     # syncs cookbooks if necessary, and triggers convergence.
     class Client
   
       SANE_PATHS = %w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin]
   
       # Clears all notifications for client run status events.
       # Primarily for testing purposes.
       def self.clear_notifications
         @run_start_notifications = nil
         @run_completed_successfully_notifications = nil
         @run_failed_notifications = nil
       end
   
       # The list of notifications to be run when the client run starts.
       def self.run_start_notifications
         @run_start_notifications ||= []
       end
   
       # The list of notifications to be run when the client run completes
       # successfully.
       def self.run_completed_successfully_notifications
         @run_completed_successfully_notifications ||= []
       end
   
       # The list of notifications to be run when the client run fails.
       def self.run_failed_notifications
         @run_failed_notifications ||= []
       end
   
       # Add a notification for the 'client run started' event. The notification
       # is provided as a block. The current Chef::RunStatus object will be passed
       # to the notification_block when the event is triggered.
       def self.when_run_starts(¬ification_block)
         run_start_notifications << notification_block
       end
   
       # Add a notification for the 'client run success' event. The notification
       # is provided as a block. The current Chef::RunStatus object will be passed
       # to the notification_block when the event is triggered.
       def self.when_run_completes_successfully(¬ification_block)
         run_completed_successfully_notifications << notification_block
       end
   
       # Add a notification for the 'client run failed' event. The notification
       # is provided as a block. The current Chef::RunStatus is passed to the
       # notification_block when the event is triggered.
       def self.when_run_fails(¬ification_block)
         run_failed_notifications << notification_block
       end
   
       # Callback to fire notifications that the Chef run is starting
       def run_started
         self.class.run_start_notifications.each do |notification|
           notification.call(run_status)
         end
       end
   
       # Callback to fire notifications that the run completed successfully
       def run_completed_successfully
         self.class.run_completed_successfully_notifications.each do |notification|
           notification.call(run_status)
         end
       end
   
       # Callback to fire notifications that the Chef run failed
       def run_failed
         self.class.run_failed_notifications.each do |notification|
           notification.call(run_status)
         end
       end
   
       attr_accessor :node
       attr_accessor :ohai
       attr_accessor :rest
       attr_accessor :runner
   
       #--
       # TODO: timh/cw: 5-19-2010: json_attribs should be moved to RunContext?
       attr_reader :json_attribs
   
       attr_reader :run_status
   
       # Creates a new Chef::Client.
       def initialize(json_attribs=nil)
         @json_attribs = json_attribs
         @node = nil
         @run_status = nil
         @runner = nil
         @ohai = Ohai::System.new
       end
   
       # Do a full run for this Chef::Client.  Calls:
       #
       #  * run_ohai - Collect information about the system
       #  * build_node - Get the last known state, merge with local changes
       #  * register - If not in solo mode, make sure the server knows about this client
       #  * sync_cookbooks - If not in solo mode, populate the local cache with the node's cookbooks
       #  * converge - Bring this system up to date
       #
       # === Returns
       # true:: Always returns true.
       def run
         run_context = nil
   
         Chef::Log.info("*** Chef #{Chef::VERSION} ***")
         enforce_path_sanity
         run_ohai
         register unless Chef::Config[:solo]
         build_node
   
         begin
   
           run_status.start_clock
           Chef::Log.info("Starting Chef Run for #{node.name}")
           run_started
   
           run_context = setup_run_context
           converge(run_context)
           save_updated_node
   
           run_status.stop_clock
           Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds")
           run_completed_successfully
           true
         rescue Exception => e
           run_status.stop_clock
           run_status.exception = e
           run_failed
           Chef::Log.debug("Re-raising exception: #{e.class} - #{e.message}\n#{e.backtrace.join("\n  ")}")
           raise
         ensure
           run_status = nil
         end
         true
       end
   
   
       # Configures the Chef::Cookbook::FileVendor class to fetch file from the
       # server or disk as appropriate, creates the run context for this run, and
       # sanity checks the cookbook collection.
       #===Returns
       # Chef::RunContext:: the run context for this run.
       def setup_run_context
         if Chef::Config[:solo]
           Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) }
           run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(Chef::CookbookLoader.new(Chef::Config[:cookbook_path])))
         else
           Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, rest) }
           cookbook_hash = sync_cookbooks
           run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(cookbook_hash))
         end
         run_status.run_context = run_context
         run_context.load(@run_list_expansion)
         assert_cookbook_path_not_empty(run_context)
         run_context
       end
   
       def save_updated_node
         unless Chef::Config[:solo]
           Chef::Log.debug("Saving the current state of node #{node_name}")
           @node.save
         end
       end
   
       def run_ohai
         ohai.all_plugins
       end
   
       def node_name
         name = Chef::Config[:node_name] || ohai[:fqdn] || ohai[:hostname]
         Chef::Config[:node_name] = name
   
         unless name
           msg = "Unable to determine node name: configure node_name or configure the system's hostname and fqdn"
           raise Chef::Exceptions::CannotDetermineNodeName, msg
         end
   
         name
       end
   
       # Builds a new node object for this client.  Starts with querying for the FQDN of the current
       # host (unless it is supplied), then merges in the facts from Ohai.
       #
       # === Returns
       # node:: Returns the created node object, also stored in @node
       def build_node
         Chef::Log.debug("Building node object for #{node_name}")
   
         if Chef::Config[:solo]
           @node = Chef::Node.build(node_name)
         else
           @node = Chef::Node.find_or_create(node_name)
         end
   
         # Allow user to override the environment of a node by specifying
         # a config parameter.
         if Chef::Config[:environment] && !Chef::Config[:environment].chop.empty?
           @node.chef_environment(Chef::Config[:environment])
         end
   
         # consume_external_attrs may add items to the run_list. Save the
         # expanded run_list, which we will pass to the server later to
         # determine which versions of cookbooks to use.
         @node.reset_defaults_and_overrides
         @node.consume_external_attrs(ohai.data, @json_attribs)
         if Chef::Config[:solo]
           @run_list_expansion = @node.expand!('disk')
         else
           @run_list_expansion = @node.expand!('server')
         end
   
         # @run_list_expansion is a RunListExpansion.
         #
         # Convert @expanded_run_list, which is an
         # Array of Hashes of the form
         #   {:name => NAME, :version_constraint => Chef::VersionConstraint },
         # into @expanded_run_list_with_versions, an
         # Array of Strings of the form
         #   "#{NAME}@#{VERSION}"
         @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings
   
         Chef::Log.info("Run List is [#{@node.run_list}]")
         Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]")
   
         @run_status = Chef::RunStatus.new(@node)
   
         @node
       end
   
       #
       # === Returns
       # rest:: returns Chef::REST connection object
       def register(client_name=node_name, config=Chef::Config)
         if File.exists?(config[:client_key])
           Chef::Log.debug("Client key #{config[:client_key]} is present - skipping registration")
         else
           Chef::Log.info("Client key #{config[:client_key]} is not present - registering")
           Chef::REST.new(config[:client_url], config[:validation_client_name], config[:validation_key]).register(client_name, config[:client_key])
         end
         # We now have the client key, and should use it from now on.
         self.rest = Chef::REST.new(config[:chef_server_url], client_name, config[:client_key])
       end
   
       # Sync_cookbooks eagerly loads all files except files and
       # templates.  It returns the cookbook_hash -- the return result
       # from /environments/#{node.chef_environment}/cookbook_versions,
       # which we will use for our run_context.
       #
       # === Returns
       # Hash:: The hash of cookbooks with download URLs as given by the server
       def sync_cookbooks
         Chef::Log.debug("Synchronizing cookbooks")
         cookbook_hash = rest.post_rest("environments/#{@node.chef_environment}/cookbook_versions",
                                        {:run_list => @expanded_run_list_with_versions})
         Chef::CookbookVersion.sync_cookbooks(cookbook_hash)
   
         # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks
         Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
   
         cookbook_hash
       end
   
       # Converges the node.
       #
       # === Returns
       # true:: Always returns true
       def converge(run_context)
         Chef::Log.debug("Converging node #{node_name}")
         @runner = Chef::Runner.new(run_context)
         runner.converge
         true
       end
   
       def enforce_path_sanity(env=ENV)
         if Chef::Config[:enforce_path_sanity] && RUBY_PLATFORM !~ /mswin|mingw32|windows/
           existing_paths = env["PATH"].split(':')
           SANE_PATHS.each do |sane_path|
             unless existing_paths.include?(sane_path)
               env_path = env["PATH"].dup
               env_path << ':' unless env["PATH"].empty?
               env_path << sane_path
               env["PATH"] = env_path
             end
           end
         end
       end
   
       private
   
       def directory_not_empty?(path)
         File.exists?(path) && (Dir.entries(path).size > 2)
       end
   
       def is_last_element?(index, object)
         object.kind_of?(Array) ? index == object.size - 1 : true
       end
   
       def assert_cookbook_path_not_empty(run_context)
         if Chef::Config[:solo]
           # Check for cookbooks in the path given
           # Chef::Config[:cookbook_path] can be a string or an array
           # if it's an array, go through it and check each one, raise error at the last one if no files are found
           Chef::Log.debug "Loading from cookbook_path: #{Array(Chef::Config[:cookbook_path]).map { |path| File.expand_path(path) }.join(', ')}"
           Array(Chef::Config[:cookbook_path]).each_with_index do |cookbook_path, index|
             if directory_not_empty?(cookbook_path)
               break
             else
               msg = "No cookbook found in #{Chef::Config[:cookbook_path].inspect}, make sure cookbook_path is set correctly."
               Chef::Log.fatal(msg)
               raise Chef::Exceptions::CookbookNotFound, msg if is_last_element?(index, Chef::Config[:cookbook_path])
             end
           end
         else
           Chef::Log.warn("Node #{node_name} has an empty run list.") if run_context.node.run_list.empty?
         end
   
       end
     end
   end
   
   # HACK cannot load this first, but it must be loaded.
   require 'chef/cookbook_loader'
   require 'chef/cookbook_version'
   

lib/chef/provider/group/pw.rb

   #
   # Author:: Stephen Haynes ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Provider
       class Group
         class Pw < Chef::Provider::Group
           
           def load_current_resource
             super
             raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/pw for #{@new_resource}" unless ::File.exists?("/usr/sbin/pw")
           end
           
           # Create the group
           def create_group
             command = "pw groupadd"
             command << set_options
             command << set_members_option
             run_command(:command => command)
           end
           
           # Manage the group when it already exists
           def manage_group
             command = "pw groupmod"
             command << set_options
             command << set_members_option
             run_command(:command => command)
           end
           
           # Remove the group
           def remove_group
             run_command(:command => "pw groupdel #{@new_resource.group_name}")
           end
           
           # Little bit of magic as per Adam's useradd provider to pull and assign the command line flags
           #
           # ==== Returns
           # :: A string containing the option and then the quoted value
           def set_options
             opts = " #{@new_resource.group_name}"
             if @new_resource.gid && (@current_resource.gid != @new_resource.gid)
               Chef::Log.debug("#{@new_resource}: current gid (#{@current_resource.gid}) doesnt match target gid (#{@new_resource.gid}), changing it")
               opts << " -g '#{@new_resource.gid}'"
             end
             opts
           end
   
           # Set the membership option depending on the current resource states
           def set_members_option
             opt = ""
             unless @new_resource.members.empty?
               opt << " -M #{@new_resource.members.join(',')}"
               Chef::Log.debug("#{@new_resource} setting group members to #{@new_resource.members.join(', ')}")
             else
               # New member list is empty so we should delete any old group members
               unless @current_resource.members.empty?
                 opt << " -d #{@current_resource.members.join(',')}"
                 Chef::Log.debug("#{@new_resource} removing group members #{@current_resource.members.join(', ')}")
               else
                 Chef::Log.debug("#{@new_resource} not changing group members, the group has no members")
               end
             end
             opt
           end
           
         end
       end
     end
   end

lib/chef/monkey_patches/tempfile.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   # == Tempfile (Patch)
   # Tempfile has a horrible bug where it causes an IOError: closed stream in its
   # finalizer, leading to intermittent application crashes with confusing stack
   # traces. Here we monkey patch the fix into place. You can track the bug on
   # ruby's redmine: http://redmine.ruby-lang.org/issues/show/3119
   #
   # The patch is slightly different for Ruby 1.8 and Ruby 1.9, both patches are
   # included here.
   class Tempfile # :nodoc:
     # Tempfile has changes between 1.8.x and 1.9.x
     # so we monkey patch separately
     if RUBY_VERSION =~ /^1\.8/
       def unlink
         # keep this order for thread safeness
         begin
           File.unlink(@tmpname) if File.exist?(@tmpname)
           @@cleanlist.delete(@tmpname)
           @tmpname = nil
           ObjectSpace.undefine_finalizer(self)
         rescue Errno::EACCES
           # may not be able to unlink on Windows; just ignore
         end
       end
       alias delete unlink
   
   
     # There is a patch for this, to be merged into 1.9 at some point.
     # When that happens, we'll want to also check the RUBY_PATCHLEVEL
     elsif RUBY_VERSION =~ /^1\.9/
       def unlink
         # keep this order for thread safeness
         return unless @tmpname
         begin
           if File.exist?(@tmpname)
             File.unlink(@tmpname)
           end
           # remove tmpname from remover
           @data[0] = @data[2] = nil
           @tmpname = nil
         rescue Errno::EACCES
           # may not be able to unlink on Windows; just ignore
         end
       end
       alias delete unlink
     end
   end

lib/chef/cookbook_loader.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/config'
   require 'chef/exceptions'
   require 'chef/cookbook/cookbook_version_loader'
   require 'chef/cookbook_version'
   require 'chef/cookbook/chefignore'
   require 'chef/cookbook/metadata'
   
   class Chef
     class CookbookLoader
   
       attr_accessor :metadata
       attr_reader :cookbooks_by_name
       attr_reader :merged_cookbooks
       attr_reader :cookbook_paths
   
       include Enumerable
   
       def initialize(*repo_paths)
         @repo_paths = repo_paths.flatten
         raise ArgumentError, "You must specify at least one cookbook repo path" if @repo_paths.empty?
         @cookbooks_by_name = Mash.new
         @loaded_cookbooks = {}
         @metadata = Mash.new
         @cookbooks_paths = Hash.new {|h,k| h[k] = []} # for deprecation warnings
   
         # Used to track which cookbooks appear in multiple places in the cookbook repos
         # and are merged in to a single cookbook by file shadowing. This behavior is
         # deprecated, so users of this class may issue warnings to the user by checking
         # this variable
         @merged_cookbooks = []
   
         load_cookbooks
       end
   
       def merged_cookbook_paths # for deprecation warnings
         merged_cookbook_paths = {}
         @merged_cookbooks.each {|c| merged_cookbook_paths[c] = @cookbooks_paths[c]}
         merged_cookbook_paths
       end
   
       def load_cookbooks
         cookbook_settings = Hash.new
         @repo_paths.each do |repo_path|
           repo_path = File.expand_path(repo_path)
           chefignore = Cookbook::Chefignore.new(repo_path)
           Dir[File.join(repo_path, "*")].each do |cookbook_path|
             next unless File.directory?(cookbook_path)
             loader = Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore)
             loader.load_cookbooks
             next if loader.empty?
             @cookbooks_paths[loader.cookbook_name] << cookbook_path # for deprecation warnings
             if @loaded_cookbooks.key?(loader.cookbook_name)
               @merged_cookbooks << loader.cookbook_name # for deprecation warnings
               @loaded_cookbooks[loader.cookbook_name].merge!(loader)
             else
               @loaded_cookbooks[loader.cookbook_name] = loader
             end
           end
         end
   
         @loaded_cookbooks.each do |cookbook, loader|
           cookbook_version = loader.cookbook_version
           @cookbooks_by_name[cookbook] = cookbook_version
           @metadata[cookbook] = cookbook_version.metadata
         end
         @cookbooks_by_name
       end
   
       def [](cookbook)
         if @cookbooks_by_name.has_key?(cookbook.to_sym)
           @cookbooks_by_name[cookbook.to_sym]
         else
           raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook.to_s}; did you forget to add metadata to a cookbook? (http://wiki.opscode.com/display/chef/Metadata)"
         end
       end
   
       alias :fetch :[]
   
       def has_key?(cookbook_name)
         @cookbooks_by_name.has_key?(cookbook_name)
       end
       alias :cookbook_exists? :has_key?
       alias :key? :has_key?
   
       def each
         @cookbooks_by_name.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |cname|
           yield(cname, @cookbooks_by_name[cname])
         end
       end
   
       def cookbook_names
         @cookbooks_by_name.keys.sort
       end
   
       def values
         @cookbooks_by_name.values
       end
       alias :cookbooks :values
   
     end
   end

lib/chef/util/file_edit.rb

   #
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'fileutils'
   require 'tempfile'
   
   class Chef
     class Util
     	class FileEdit
   
     		private
     		
     		attr_accessor :original_pathname, :contents, :file_edited
   
     		public
     		
     		def initialize(filepath)
     			@original_pathname = filepath
     			@file_edited = false
     			
     			raise ArgumentError, "File doesn't exist" unless File.exist? @original_pathname
     			raise ArgumentError, "File is blank" unless (@contents = File.new(@original_pathname).readlines).length > 0
     		end
     		
     		#search the file line by line and match each line with the given regex
     		#if matched, replace the whole line with newline.
     		def search_file_replace_line(regex, newline)
     			search_match(regex, newline, 'r', 1)
     		end
   
     		#search the file line by line and match each line with the given regex
     		#if matched, replace the match (all occurances)  with the replace parameter
     		def search_file_replace(regex, replace)
     			search_match(regex, replace, 'r', 2)
     		end
     		
     		#search the file line by line and match each line with the given regex
     		#if matched, delete the line
     		def search_file_delete_line(regex)
     			search_match(regex, " ", 'd', 1)
     		end
     		
     		#search the file line by line and match each line with the given regex
     		#if matched, delete the match (all occurances) from the line
     		def search_file_delete(regex)
     			search_match(regex, " ", 'd', 2)
     		end
   
     		#search the file line by line and match each line with the given regex
     		#if matched, insert newline after each matching line
     		def insert_line_after_match(regex, newline)
     			search_match(regex, newline, 'i', 0)
     		end
     		 
     		#Make a copy of old_file and write new file out (only if file changed)
     		def write_file
     			
     			# file_edited is false when there was no match in the whole file and thus no contents have changed.
           if file_edited
             backup_pathname = original_pathname + ".old"
             FileUtils.cp(original_pathname, backup_pathname, :preserve => true)
             File.open(original_pathname, "w") do |newfile|
               contents.each do |line|
                 newfile.puts(line)
               end
               newfile.flush
             end
           end
           self.file_edited = false
     		end
     		
     		private
     		
     		#helper method to do the match, replace, delete, and insert operations
     		#command is the switch of delete, replace, and insert ('d', 'r', 'i')
     		#method is to control operation on whole line or only the match (1 for line, 2 for match)
     		def search_match(regex, replace, command, method)
     			
     			#convert regex to a Regexp object (if not already is one) and store it in exp.
     			exp = Regexp.new(regex)
   
     			#loop through contents and do the appropriate operation depending on 'command' and 'method'
     			new_contents = []
     			
     			contents.each do |line|
     				if line.match(exp) 
     					self.file_edited = true
     					case
     					when command == 'r'
     						new_contents << ((method == 1) ? replace : line.gsub!(exp, replace))
     					when command == 'd'
     						if method == 2
     							new_contents << line.gsub!(exp, "")
     						end
     					when command == 'i'
     						new_contents << line
     						new_contents << replace
     					end
     				else
     					new_contents << line
     				end
     			end
   
     			self.contents = new_contents
     		end
     	end
     end
   end

lib/chef/file_cache.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/mixin/params_validate'
   require 'chef/mixin/create_path'
   require 'chef/exceptions'
   require 'chef/json_compat'
   require 'fileutils'
   
   class Chef
     class FileCache
       class << self
         include Chef::Mixin::ParamsValidate
         include Chef::Mixin::CreatePath
   
         # Write a file to the File Cache.
         #
         # === Parameters
         # path:: The path to the file you want to put in the cache - should
         #   be relative to file_cache_path
         # contents:: A string with the contents you want written to the file
         #
         # === Returns
         # true
         def store(path, contents)
           validate(
             {
               :path => path,
               :contents => contents
             },
             {
               :path => { :kind_of => String },
               :contents => { :kind_of => String },
             }
           )
   
           file_path_array = File.split(path)
           file_name = file_path_array.pop
           cache_path = create_cache_path(File.join(file_path_array))
           File.open(File.join(cache_path, file_name), "w") do |io|
             io.print(contents)
           end
           true
         end
   
         # Move a file into the cache.  Useful with the REST raw file output.
         #
         # === Parameters
         # file:: The path to the file you want in the cache
         # path:: The relative name you want the new file to use
         def move_to(file, path)
           validate(
             {
               :file => file,
               :path => path
             },
             {
               :file => { :kind_of => String },
               :path => { :kind_of => String },
             }
           )
   
           file_path_array = File.split(path)
           file_name = file_path_array.pop
           if File.exists?(file) && File.writable?(file)
             FileUtils.mv(
               file,
               File.join(create_cache_path(File.join(file_path_array), true), file_name)
             )
           else
             raise RuntimeError, "Cannot move #{file} to #{path}!"
           end
         end
   
         # Read a file from the File Cache
         #
         # === Parameters
         # path:: The path to the file you want to load - should
         #   be relative to file_cache_path
         # read:: Whether to return the file contents, or the path.
         #   Defaults to true.
         #
         # === Returns
         # String:: A string with the file contents, or the path to the file.
         #
         # === Raises
         # Chef::Exceptions::FileNotFound:: If it cannot find the file in the cache
         def load(path, read=true)
           validate(
             {
               :path => path
             },
             {
               :path => { :kind_of => String }
             }
           )
           cache_path = create_cache_path(path, false)
           raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path)
           if read
             File.read(cache_path)
           else
             cache_path
           end
         end
   
         # Delete a file from the File Cache
         #
         # === Parameters
         # path:: The path to the file you want to delete - should
         #   be relative to file_cache_path
         #
         # === Returns
         # true
         def delete(path)
           validate(
             {
               :path => path
             },
             {
               :path => { :kind_of => String },
             }
           )
           cache_path = create_cache_path(path, false)
           if File.exists?(cache_path)
             File.unlink(cache_path)
           end
           true
         end
   
         # List all the files in the Cache
         #
         # === Returns
         # Array:: An array of files in the cache, suitable for use with load, delete and store
         def list
           find("**#{File::Separator}*")
         end
   
         ##
         # Find files in the cache by +glob_pattern+
         # === Returns
         # [String] - An array of file cache keys matching the glob
         def find(glob_pattern)
           keys = Array.new
           Dir[File.join(file_cache_path, glob_pattern)].each do |f|
             if File.file?(f)
               keys << f[/^#{Regexp.escape(Dir[file_cache_path].first) + File::Separator}(.+)/, 1]
             end
           end
           keys
         end
   
         # Whether or not this file exists in the Cache
         #
         # === Parameters
         # path:: The path to the file you want to check - is relative
         #   to file_cache_path
         #
         # === Returns
         # True:: If the file exists
         # False:: If it does not
         def has_key?(path)
           validate(
             {
               :path => path
             },
             {
               :path => { :kind_of => String },
             }
           )
           full_path = create_cache_path(path, false)
           if File.exists?(full_path)
             true
           else
             false
           end
         end
   
         # Create a full path to a given file in the cache. By default,
         # also creates the path if it does not exist.
         #
         # === Parameters
         # path:: The path to create, relative to file_cache_path
         # create_if_missing:: True by default - whether to create the path if it does not exist
         #
         # === Returns
         # String:: The fully expanded path
         def create_cache_path(path, create_if_missing=true)
           cache_dir = File.expand_path(File.join(file_cache_path, path))
           if create_if_missing
             create_path(cache_dir)
           else
             cache_dir
           end
         end
   
         private
   
         def file_cache_path
           Chef::Config[:file_cache_path]
         end
   
       end
     end
   end

lib/chef/resource/env.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Resource
       class Env < Chef::Resource
   
         def initialize(name, run_context=nil)
           super
           @resource_name = :env
           @key_name = name
           @value = nil
           @action = :create
           @delim = nil
           @allowed_actions.push(:create, :delete, :modify)
         end
   
         def key_name(arg=nil)
           set_or_return(
             :key_name,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def value(arg=nil)
           set_or_return(
             :value,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def delim(arg=nil)
           set_or_return(
             :delim,
             arg,
             :kind_of => [ String ]
           )
         end
       end
     end
   end

lib/chef/shef.rb

   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'singleton'
   require 'pp'
   require 'etc'
   require 'mixlib/cli'
   
   require 'chef'
   require 'chef/version'
   require 'chef/client'
   require 'chef/config'
   
   require 'chef/shef/shef_session'
   require 'chef/shef/ext'
   require 'chef/json_compat'
   
   # = Shef
   # Shef is Chef in an IRB session. Shef can interact with a Chef server via the
   # REST API, and run and debug recipes interactively.
   module Shef
     LEADERS = Hash.new("")
     LEADERS[Chef::Recipe] = ":recipe"
     LEADERS[Chef::Node]   = ":attributes"
   
     class << self
       attr_accessor :client_type
       attr_accessor :options
       attr_accessor :env
       attr_writer   :editor
     end
   
     # Start the irb REPL with shef's customizations
     def self.start
       setup_logger
       # FUGLY HACK: irb gives us no other choice.
       irb_help = [:help, :irb_help, IRB::ExtendCommandBundle::NO_OVERRIDE]
       IRB::ExtendCommandBundle.instance_variable_get(:@ALIASES).delete(irb_help)
   
       parse_opts
   
       # HACK: this duplicates the functions of IRB.start, but we have to do it
       # to get access to the main object before irb starts.
       ::IRB.setup(nil)
   
       irb = IRB::Irb.new
   
       init(irb.context.main)
   
   
       irb_conf[:IRB_RC].call(irb.context) if irb_conf[:IRB_RC]
       irb_conf[:MAIN_CONTEXT] = irb.context
   
       trap("SIGINT") do
         irb.signal_handle
       end
   
       catch(:IRB_EXIT) do
         irb.eval_input
       end
     end
   
     def self.setup_logger
       Chef::Config[:log_level] ||= :warn
       Chef::Log.init(STDERR)
       Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger
       Chef::Log.level = Chef::Config[:log_level] || :warn
     end
   
     # Shef assumes it's running whenever it is defined
     def self.running?
       true
     end
   
     # Set the irb_conf object to something other than IRB.conf
     # usful for testing.
     def self.irb_conf=(conf_hash)
       @irb_conf = conf_hash
     end
   
     def self.irb_conf
       @irb_conf || IRB.conf
     end
   
     def self.configure_irb
       irb_conf[:HISTORY_FILE] = "~/.shef_history"
       irb_conf[:SAVE_HISTORY] = 1000
   
       irb_conf[:IRB_RC] = lambda do |conf|
         m = conf.main
   
         conf.prompt_c       = "chef#{leader(m)} > "
         conf.return_format  = " => %s \n"
         conf.prompt_i       = "chef#{leader(m)} > "
         conf.prompt_n       = "chef#{leader(m)} ?> "
         conf.prompt_s       = "chef#{leader(m)}%l> "
       end
     end
   
     def self.leader(main_object)
       env_string = Shef.env ? " (#{Shef.env})" : ""
       LEADERS[main_object.class] + env_string
     end
   
     def self.session
       unless client_type.instance.node_built?
         puts "Session type: #{client_type.session_type}"
         client_type.instance.reset!
       end
       client_type.instance
     end
   
     def self.init(main)
       parse_json
       configure_irb
   
       session # trigger ohai run + session load
   
       session.node.consume_attributes(@json_attribs)
   
       Extensions.extend_context_object(main)
   
       main.version
       puts
   
       puts "run `help' for help, `exit' or ^D to quit."
       puts
       puts "Ohai2u#{greeting}!"
     end
   
     def self.greeting
       " #{Etc.getlogin}@#{Shef.session.node.fqdn}"
     rescue NameError
       ""
     end
   
     def self.parse_json
       # HACK: copied verbatim from chef/application/client, because it's not
       # reusable as written there :(
       if Chef::Config[:json_attribs]
         begin
           json_io = open(Chef::Config[:json_attribs])
         rescue SocketError => error
           fatal!("I cannot connect to #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::ENOENT => error
           fatal!("I cannot find #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::EACCES => error
           fatal!("Permissions are incorrect on #{Chef::Config[:json_attribs]}. Please chmod a+r #{Chef::Config[:json_attribs]}", 2)
         rescue Exception => error
           fatal!("Got an unexpected error reading #{Chef::Config[:json_attribs]}: #{error.message}", 2)
         end
   
         begin
           @json_attribs = Chef::JSONCompat.from_json(json_io.read)
         rescue JSON::ParserError => error
           fatal!("Could not parse the provided JSON file (#{Chef::Config[:json_attribs]})!: " + error.message, 2)
         end
       end
     end
   
     def self.fatal!(message, exit_status)
       Chef::Log.fatal(message)
       exit exit_status
     end
   
     def self.client_type
       type = Shef::StandAloneSession
       type = Shef::SoloSession   if Chef::Config[:shef_solo]
       type = Shef::ClientSession if Chef::Config[:client]
       type = Shef::DoppelGangerSession if Chef::Config[:doppelganger]
       type
     end
   
     def self.parse_opts
       @options = Options.new
       @options.parse_opts
     end
   
     def self.editor
       @editor || Chef::Config[:editor] || ENV['EDITOR']
     end
   
     class Options
       include Mixlib::CLI
   
       def self.footer(text=nil)
         @footer = text if text
         @footer
       end
   
       banner("shef #{Chef::VERSION}\n\nUsage: shef [NAMED_CONF] (OPTIONS)")
   
       footer(<<-FOOTER)
   When no CONFIG is specified, shef attempts to load a default configuration file:
   * If a NAMED_CONF is given, shef will load ~/.chef/NAMED_CONF/shef.rb
   * If no NAMED_CONF is given shef will load ~/.chef/shef.rb if it exists
   * Shef falls back to loading /etc/chef/client.rb or /etc/chef/solo.rb if -z or
     -s options are given and no shef.rb can be found. 
   FOOTER
   
       option :config_file,
         :short => "-c CONFIG",
         :long  => "--config CONFIG",
         :description => "The configuration file to use"
   
       option :help,
         :short        => "-h",
         :long         => "--help",
         :description  => "Show this message",
         :on           => :tail,
         :boolean      => true,
         :proc         => proc { print_help }
   
       option :log_level,
         :short  => "-l LOG_LEVEL",
         :long   => '--log-level LOG_LEVEL',
         :description => "Set the logging level",
         :proc         => proc { |level| Chef::Log.level = level.to_sym }
   
       option :standalone,
         :short        => "-a",
         :long         => "--standalone",
         :description  => "standalone shef session",
         :default      => true,
         :boolean      => true
   
       option :shef_solo,
         :short        => "-s",
         :long         => "--solo",
         :description  => "chef-solo shef session",
         :boolean      => true,
         :proc         => proc {Chef::Config[:solo] = true}
   
       option :client,
         :short        => "-z",
         :long         => "--client",
         :description  => "chef-client shef session",
         :boolean      => true
   
       option :json_attribs,
         :short => "-j JSON_ATTRIBS",
         :long => "--json-attributes JSON_ATTRIBS",
         :description => "Load attributes from a JSON file or URL",
         :proc => nil
   
       option :chef_server_url,
         :short => "-S CHEFSERVERURL",
         :long => "--server CHEFSERVERURL",
         :description => "The chef server URL",
         :proc => nil
   
       option :version,
         :short        => "-v",
         :long         => "--version",
         :description  => "Show chef version",
         :boolean      => true,
         :proc         => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
         :exit         => 0
   
       def self.print_help
         instance = new
         instance.parse_options([])
         puts instance.opt_parser
         puts
         puts footer
         puts
         exit 1
       end
   
       def self.setup!
         self.new.parse_opts
       end
   
       def parse_opts
         remainder = parse_options
         environment = remainder.first
         # We have to nuke ARGV to make sure irb's option parser never sees it.
         # otherwise, IRB complains about command line switches it doesn't recognize.
         ARGV.clear
         config[:config_file] = config_file_for_shef_mode(environment)
         config_msg = config[:config_file] || "none (standalone shef session)"
         puts "loading configuration: #{config_msg}"
         Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
         Chef::Config.merge!(config)
       end
   
       private
   
       def config_file_for_shef_mode(environment)
         if config[:config_file]
           config[:config_file]
         elsif environment
           Shef.env = environment
           config_file_to_try = ::File.join(ENV['HOME'], '.chef', environment, 'shef.rb')
           unless ::File.exist?(config_file_to_try)
             puts "could not find shef config for environment #{environment} at #{config_file_to_try}"
             exit 1
           end
           config_file_to_try
         elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'shef.rb'))
           File.join(ENV['HOME'], '.chef', 'shef.rb')
         elsif config[:solo]
           "/etc/chef/solo.rb"
         elsif config[:client]
           "/etc/chef/client.rb"
         else
           nil
         end
       end
   
     end
   
   end

lib/chef/resource/http_request.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class HttpRequest < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :http_request
           @message = name
           @url = nil
           @action = :get
           @headers = {}
           @allowed_actions.push(:get, :put, :post, :delete, :head, :options)
         end
         
         def url(args=nil)
           set_or_return(
             :url,
             args,
             :kind_of => String
           )
         end
         
         def message(args=nil)
           set_or_return(
             :message,
             args,
             :kind_of => Object
           )
         end
   
         def headers(args=nil)
           set_or_return(
             :headers,
             args,
             :kind_of => Hash
           )
         end
         
       end
     end
   end

lib/chef/knife/ssh.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class Ssh < Knife
   
         deps do
           require 'net/ssh'
           require 'net/ssh/multi'
           require 'readline'
           require 'chef/search/query'
           require 'chef/mixin/command'
         end
   
         attr_writer :password
   
         banner "knife ssh QUERY COMMAND (options)"
   
         option :concurrency,
           :short => "-C NUM",
           :long => "--concurrency NUM",
           :description => "The number of concurrent connections",
           :default => nil,
           :proc => lambda { |o| o.to_i }
   
         option :attribute,
           :short => "-a ATTR",
           :long => "--attribute ATTR",
           :description => "The attribute to use for opening the connection - default is fqdn",
           :default => "fqdn"
   
         option :manual,
           :short => "-m",
           :long => "--manual-list",
           :boolean => true,
           :description => "QUERY is a space separated list of servers",
           :default => false
   
         option :ssh_user,
           :short => "-x USERNAME",
           :long => "--ssh-user USERNAME",
           :description => "The ssh username"
   
         option :ssh_password,
           :short => "-P PASSWORD",
           :long => "--ssh-password PASSWORD",
           :description => "The ssh password"
   
         option :ssh_port,
           :short => "-p PORT",
           :long => "--ssh-port PORT",
           :description => "The ssh port",
           :default => "22",
           :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
   
         option :identity_file,
           :short => "-i IDENTITY_FILE",
           :long => "--identity-file IDENTITY_FILE",
           :description => "The SSH identity file used for authentication"
   
         option :no_host_key_verify,
           :long => "--no-host-key-verify",
           :description => "Disable host key verification",
           :boolean => true,
           :default => false
   
         def session
           config[:on_error] ||= :skip
           ssh_error_handler = Proc.new do |server|
             if config[:manual]
               node_name = server.host
             else
               @action_nodes.each do |n|
                 node_name = n if format_for_display(n)[config[:attribute]] == server.host
               end
             end
             case config[:on_error]
             when :skip
               ui.warn "Failed to connect to #{node_name} -- #{$!.class.name}: #{$!.message}"
               $!.backtrace.each { |l| Chef::Log.debug(l) }
             when :raise
               #Net::SSH::Multi magic to force exception to be re-raised.
               throw :go, :raise
             end
           end
   
           @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency], :on_error => ssh_error_handler)
         end
   
         def configure_session
           list = case config[:manual]
                  when true
                    @name_args[0].split(" ")
                  when false
                    r = Array.new
                    q = Chef::Search::Query.new
                    @action_nodes = q.search(:node, @name_args[0])[0]
                    @action_nodes.each do |item|
                      i = format_for_display(item)[config[:attribute]]
                      r.push(i) unless i.nil?
                    end
                    r
                  end
           (ui.fatal("No nodes returned from search!"); exit 10) if list.length == 0
           session_from_list(list)
         end
   
         def session_from_list(list)
           list.each do |item|
             Chef::Log.debug("Adding #{item}")
   
             hostspec = config[:ssh_user] ? "#{config[:ssh_user]}@#{item}" : item
             session_opts = {}
             session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file]
             session_opts[:password] = config[:ssh_password] if config[:ssh_password]
             session_opts[:port] = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
             session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
   
             if config[:no_host_key_verify]
               session_opts[:paranoid] = false
               session_opts[:user_known_hosts_file] = "/dev/null"
             end
   
             session.use(hostspec, session_opts)
   
             @longest = item.length if item.length > @longest
           end
   
           session
         end
   
         def fixup_sudo(command)
           command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
         end
   
         def print_data(host, data)
           if data =~ /\n/
             data.split(/\n/).each { |d| print_data(host, d) }
           else
             padding = @longest - host.length
             str = ui.color(host, :cyan) + (" " * (padding + 1)) + data
             ui.msg(str)
           end
         end
   
         def ssh_command(command, subsession=nil)
           subsession ||= session
           command = fixup_sudo(command)
           subsession.open_channel do |ch|
             ch.request_pty
             ch.exec command do |ch, success|
               raise ArgumentError, "Cannot execute #{command}" unless success
               ch.on_data do |ichannel, data|
                 print_data(ichannel[:host], data)
                 if data =~ /^knife sudo password: /
                   ichannel.send_data("#{get_password}\n")
                 end
               end
             end
           end
           session.loop
         end
   
         def get_password
           @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
         end
   
         # Present the prompt and read a single line from the console. It also
         # detects ^D and returns "exit" in that case. Adds the input to the
         # history, unless the input is empty. Loops repeatedly until a non-empty
         # line is input.
         def read_line
           loop do
             command = reader.readline("#{ui.color('knife-ssh>', :bold)} ", true)
   
             if command.nil?
               command = "exit"
               puts(command)
             else
               command.strip!
             end
   
             unless command.empty?
               return command
             end
           end
         end
   
         def reader
           Readline
         end
   
         def interactive
           puts "Connected to #{ui.list(session.servers_for.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
           puts
           puts "To run a command on a list of servers, do:"
           puts "  on SERVER1 SERVER2 SERVER3; COMMAND"
           puts "  Example: on latte foamy; echo foobar"
           puts
           puts "To exit interactive mode, use 'quit!'"
           puts
           while 1
             command = read_line
             case command
             when 'quit!'
               puts 'Bye!'
               break
             when /^on (.+?); (.+)$/
               raw_list = $1.split(" ")
               server_list = Array.new
               session.servers.each do |session_server|
                 server_list << session_server if raw_list.include?(session_server.host)
               end
               command = $2
               ssh_command(command, session.on(*server_list))
             else
               ssh_command(command)
             end
           end
         end
   
         def screen
           tf = Tempfile.new("knife-ssh-screen")
           if File.exist? "#{ENV["HOME"]}/.screenrc"
             tf.puts("source #{ENV["HOME"]}/.screenrc")
           end
           tf.puts("caption always '%-Lw%{= BW}%50>%n%f* %t%{-}%+Lw%<'")
           tf.puts("hardstatus alwayslastline 'knife ssh #{@name_args[0]}'")
           window = 0
           session.servers_for.each do |server|
             tf.print("screen -t \"#{server.host}\" #{window} ssh ")
             tf.print("-i #{config[:identity_file]} ") if config[:identity_file]
             server.user ? tf.puts("#{server.user}@#{server.host}") : tf.puts(server.host)
             window += 1
           end
           tf.close
           exec("screen -c #{tf.path}")
         end
   
         def tmux
           ssh_dest = lambda do |server|
             identity = "-i #{config[:identity_file]} " if config[:identity_file]
             prefix = server.user ? "#{server.user}@" : ""
             "'ssh #{identity}#{prefix}#{server.host}'"
           end
   
           new_window_cmds = lambda do
             if session.servers_for.size > 1
               [""] + session.servers_for[1..-1].map do |server|
                 "new-window -a -n '#{server.host}' #{ssh_dest.call(server)}"
               end
             else
               []
             end.join(" \\; ")
           end
   
           tmux_name = "'knife ssh #{@name_args[0].gsub(/:/,'=')}'"
           begin
             server = session.servers_for.first
             cmd = ["tmux new-session -d -s #{tmux_name}",
                    "-n '#{server.host}'", ssh_dest.call(server),
                    new_window_cmds.call].join(" ")
             Chef::Mixin::Command.run_command(:command => cmd)
             exec("tmux attach-session -t #{tmux_name}")
           rescue Chef::Exceptions::Exec
           end
         end
   
         def macterm
           begin
             require 'appscript'
           rescue LoadError
             STDERR.puts "you need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
             raise
           end
   
           Appscript.app("/Applications/Utilities/Terminal.app").windows.first.activate
           Appscript.app("System Events").application_processes["Terminal.app"].keystroke("n", :using=>:command_down)
           term = Appscript.app('Terminal')
           window = term.windows.first.get
   
           (session.servers_for.size - 1).times do |i|
             window.activate
             Appscript.app("System Events").application_processes["Terminal.app"].keystroke("t", :using=>:command_down)
           end
   
           session.servers_for.each_with_index do |server, tab_number|
             cmd = "unset PROMPT_COMMAND; echo -e \"\\033]0;#{server.host}\\007\"; ssh #{server.user ? "#{server.user}@#{server.host}" : server.host}"
             Appscript.app('Terminal').do_script(cmd, :in => window.tabs[tab_number + 1].get)
           end
         end
   
         def configure_attribute
           config[:attribute] = (config[:attribute] ||
                                 Chef::Config[:knife][:ssh_attribute] ||
                                 "fqdn").strip
         end
   
         def csshx
           csshx_cmd = "csshX"
           session.servers_for.each do |server|
             csshx_cmd << " #{server.user ? "#{server.user}@#{server.host}" : server.host}"
           end
           exec(csshx_cmd)
         end
   
         def configure_user
           config[:ssh_user] = (config[:ssh_user] ||
                                Chef::Config[:knife][:ssh_user])
           config[:ssh_user].strip! unless config[:ssh_user].nil?
         end
   
         def configure_identity_file
           config[:identity_file] = (config[:identity_file] || Chef::Config[:knife][:ssh_identity_file])
           config[:identity_file].strip! unless config[:identity_file].nil?
         end
   
         def run
           extend Chef::Mixin::Command
   
           @longest = 0
   
           configure_attribute
           configure_user
           configure_identity_file
           configure_session
   
           case @name_args[1]
           when "interactive"
             interactive
           when "screen"
             screen
           when "tmux"
             tmux
           when "macterm"
             macterm
           when "csshx"
             csshx
           else
             ssh_command(@name_args[1..-1].join(" "))
           end
   
           session.close
         end
   
       end
     end
   end
   

lib/chef/provider/service/init.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/simple'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Init < Chef::Provider::Service::Simple
           
           def initialize(new_resource, run_context)
             super
             @init_command = "/etc/init.d/#{@new_resource.service_name}"
           end
   
           def start_service
             if @new_resource.start_command
               super
             else
               run_command(:command => "#{@init_command} start")
             end
           end
   
           def stop_service
             if @new_resource.stop_command
               super
             else
               run_command(:command => "#{@init_command} stop")
             end
           end
   
           def restart_service
             if @new_resource.restart_command
               super
             elsif @new_resource.supports[:restart]
               run_command(:command => "#{@init_command} restart")
             else
               stop_service
               sleep 1
               start_service
             end
           end
   
           def reload_service
             if @new_resource.reload_command
               super
             elsif @new_resource.supports[:reload]
               run_command(:command => "#{@init_command} reload")
             end
           end
         end
       end
     end
   end

lib/chef/knife/configure_client.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ConfigureClient < Knife
         banner "knife configure client DIRECTORY"
   
         def run
           unless @config_dir = @name_args[0]
             ui.fatal "You must provide the directory to put the files in"
             show_usage
             exit(1)
           end
   
           ui.info("Creating client configuration")
           FileUtils.mkdir_p(@config_dir)
           ui.info("Writing client.rb")
           File.open(File.join(@config_dir, "client.rb"), "w") do |file|
             file.puts('log_level        :info')
             file.puts('log_location     STDOUT')
             file.puts("chef_server_url  '#{Chef::Config[:chef_server_url]}'")
             file.puts("validation_client_name '#{Chef::Config[:validation_client_name]}'")
           end
           ui.info("Writing validation.pem")
           File.open(File.join(@config_dir, 'validation.pem'), "w") do |validation|
             validation.puts(IO.read(Chef::Config[:validation_key]))
           end
         end
   
       end
     end
   end

lib/chef/knife/tag_delete.rb

   #
   # Author:: Ryan Davis ()
   # Author:: Daniel DeLeo ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2011 Ryan Davis and Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class TagDelete < Knife
   
         deps do
           require 'chef/node'
         end
   
         banner "knife tag delete NODE TAG ..."
   
         def run
           name = @name_args[0]
           tags = @name_args[1..-1]
   
           if name.nil? || tags.nil? || tags.empty?
             show_usage
             ui.fatal("You must specify a node name and at least one tag.")
             exit 1
           end
   
           node = Chef::Node.load name
           deleted_tags = Array.new
           tags.each do |tag|
             unless node.tags.delete(tag).nil?
               deleted_tags << tag
             end
           end
           node.save
           message = if deleted_tags.empty?
                       "Nothing has changed. The tags requested to be deleted do not exist."
                     else
                       "Deleted the following tags: #{deleted_tags.join(", ")}."
                     end
           ui.info(message)
         end
       end
     end
   end

lib/chef/resource/remote_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/file'
   
   class Chef
     class Resource
       class RemoteFile < Chef::Resource::File
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :remote_file
           @action = "create"
           @source = ::File.basename(name)
           @cookbook = nil
         end
         
         def source(args=nil)
           set_or_return(
             :source,
             args,
             :kind_of => String
           )
         end
         
         def cookbook(args=nil)
           set_or_return(
             :cookbook,
             args,
             :kind_of => String
           )
         end
   
         def checksum(args=nil)
           set_or_return(
             :checksum,
             args,
             :kind_of => String
           )
         end
   
         # The provider that should be used for this resource.
         # === Returns:
         # Chef::Provider::RemoteFile    when the source is an absolute URI, like
         #                               http://www.google.com/robots.txt
         # Chef::Provider::CookbookFile  when the source is a relative URI, like
         #                               'myscript.pl', 'dir/config.conf'
         def provider
           if absolute_uri?(source)
             Chef::Provider::RemoteFile
           else
             Chef::Log.warn("remote_file is deprecated for fetching files from cookbooks. Use cookbook_file instead")
             Chef::Log.warn("From #{self.to_s} on #{source_line}")
             Chef::Provider::CookbookFile
           end
         end
   
         private
         
         def absolute_uri?(source)
           URI.parse(source).absolute?
         rescue URI::InvalidURIError
           false
         end
   
       end
     end
   end

lib/chef/knife/cookbook_show.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookShow < Knife
   
         deps do
           require 'chef/json_compat'
           require 'uri'
           require 'chef/cookbook_version'
         end
   
         banner "knife cookbook show COOKBOOK [VERSION] [PART] [FILENAME] (options)"
   
         option :fqdn,
          :short => "-f FQDN",
          :long => "--fqdn FQDN",
          :description => "The FQDN of the host to see the file for"
   
         option :platform,
          :short => "-p PLATFORM",
          :long => "--platform PLATFORM",
          :description => "The platform to see the file for"
   
         option :platform_version,
          :short => "-V VERSION",
          :long => "--platform-version VERSION",
          :description => "The platform version to see the file for"
   
         option :with_uri,
           :short => "-w",
           :long => "--with-uri",
           :description => "Show corresponding URIs"
   
         def run 
           case @name_args.length
           when 4 # We are showing a specific file
             node = Hash.new
             node[:fqdn] = config[:fqdn] if config.has_key?(:fqdn)
             node[:platform] = config[:platform] if config.has_key?(:platform)
             node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version)
   
             class << node
               def attribute?(name)
                 has_key?(name)
               end
             end
   
             cookbook_name, segment, filename = @name_args[0], @name_args[2], @name_args[3]
             cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1]
   
             cookbook = rest.get_rest("cookbooks/#{cookbook_name}/#{cookbook_version}")
             manifest_entry = cookbook.preferred_manifest_record(node, segment, filename)
             temp_file = rest.get_rest(manifest_entry[:url], true)
   
             # the temp file is cleaned up elsewhere
             temp_file.open if temp_file.closed?
             pretty_print(temp_file.read)
   
           when 3 # We are showing a specific part of the cookbook
             cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1]
             result = rest.get_rest("cookbooks/#{@name_args[0]}/#{cookbook_version}")
             output(result.manifest[@name_args[2]])
           when 2 # We are showing the whole cookbook data
             cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1]
             output(rest.get_rest("cookbooks/#{@name_args[0]}/#{cookbook_version}"))
           when 1 # We are showing the cookbook versions (all of them)
             cookbook_name = @name_args[0]
             env           = config[:environment]
             api_endpoint  = env ? "environments/#{env}/cookbooks/#{cookbook_name}" : "cookbooks/#{cookbook_name}"
             output(format_cookbook_list_for_display(rest.get_rest(api_endpoint)))
           when 0
             show_usage
             ui.fatal("You must specify a cookbook name")
             exit 1
           end
         end
       end
     end
   end
   
   
   
   

lib/chef/mixin/template.rb

   #--
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'tempfile'
   require 'erubis'
   
   class Chef
     module Mixin
       module Template
         
         module ChefContext
           def node
             return @node if @node
             raise "Could not find a value for node. If you are explicitly setting variables in a template, " +
                   "include a node variable if you plan to use it."
           end
         end
         
         ::Erubis::Context.send(:include, ChefContext)
         
         # Render a template with Erubis.  Takes a template as a string, and a 
         # context hash.  
         def render_template(template, context)
           begin
             eruby = Erubis::Eruby.new(template)
             output = eruby.evaluate(context)
           rescue Object => e
             raise TemplateError.new(e, template, context)
           end
           Tempfile.open("chef-rendered-template") do |tempfile|
             tempfile.print(output)
             tempfile.close
             yield tempfile
           end
         end
         
         class TemplateError < RuntimeError
           attr_reader :original_exception, :context
           SOURCE_CONTEXT_WINDOW = 2
           
           def initialize(original_exception, template, context)
             @original_exception, @template, @context = original_exception, template, context
           end
           
           def message
             @original_exception.message
           end
           
           def line_number
             @line_number ||= $1.to_i if original_exception.backtrace.find {|line| line =~ /\(erubis\):(\d+)/ }
           end
           
           def source_location
             "on line ##{line_number}"
           end
           
           def source_listing
             @source_listing ||= begin
               line_index = line_number - 1
               beginning_line = line_index <= SOURCE_CONTEXT_WINDOW ? 0 : line_index - SOURCE_CONTEXT_WINDOW
               source_size = SOURCE_CONTEXT_WINDOW * 2 + 1
               lines = @template.split(/\n/)
               contextual_lines = lines[beginning_line, source_size]
               output = []
               contextual_lines.each_with_index do |line, index|
                 line_number = (index+beginning_line+1).to_s.rjust(3)
                 output << "#{line_number}: #{line}"
               end
               output.join("\n")
             end
           end
           
           def to_s
             "\n\n#{self.class} (#{message}) #{source_location}:\n\n" +
               "#{source_listing}\n\n  #{original_exception.backtrace.join("\n  ")}\n\n"
           end
         end
       end
     end
   end

lib/chef/resource/yum_package.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/package'
   require 'chef/provider/package/yum'
   
   class Chef
     class Resource
       class YumPackage < Chef::Resource::Package
   
         def initialize(name, run_context=nil)
           super
           @resource_name = :yum_package
           @provider = Chef::Provider::Package::Yum
           @flush_cache = { :before => false, :after => false }
           @allow_downgrade = false
         end
   
         # Install a specific arch
         def arch(arg=nil)
           set_or_return(
             :arch,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def flush_cache(args={})
           if args.is_a? Array
             args.each { |arg| @flush_cache[arg] = true }
           elsif args.any?
             @flush_cache = args
           else
             @flush_cache
           end
         end
   
         def allow_downgrade(arg=nil)
           set_or_return(
             :allow_downgrade,
             arg,
             :kind_of => [ TrueClass, FalseClass ]
           )
         end
   
       end
     end
   end

lib/chef/resource/script.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/execute'
   
   class Chef
     class Resource
       class Script < Chef::Resource::Execute
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :script
           @command = name
           @code = nil
           @interpreter = nil
           @flags = nil
         end
         
         def code(arg=nil)
           set_or_return(
             :code,
             arg,
             :kind_of => [ String ]
           )
         end
         
         def interpreter(arg=nil)
           set_or_return(
             :interpreter,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def flags(arg=nil)
           set_or_return(
             :flags,
             arg,
             :kind_of => [ String ]
           )
         end
   
       end
     end
   end

lib/chef/mixin/language_include_attribute.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   
   class Chef
     module Mixin
       module LanguageIncludeAttribute
   
         # Loads the attribute file specified by the short name of the
         # file, e.g., loads specified cookbook's
         #   "attributes/mailservers.rb"
         # if passed
         #   "mailservers"
         def include_attribute(*fully_qualified_attribute_short_filenames)
           if self.kind_of?(Chef::Node)
             node = self
           else
             node = @node
           end
   
           fully_qualified_attribute_short_filenames.flatten.each do |fully_qualified_attribute_short_filename|
             if node.run_state[:seen_attributes].has_key?(fully_qualified_attribute_short_filename)
               Chef::Log.debug("I am not loading attribute file #{fully_qualified_attribute_short_filename}, because I have already seen it.")
               next
             end
   
             Chef::Log.debug("Loading Attribute #{fully_qualified_attribute_short_filename}")
             node.run_state[:seen_attributes][fully_qualified_attribute_short_filename] = true
   
             if amatch = fully_qualified_attribute_short_filename.match(/(.+?)::(.+)/)
               cookbook_name = amatch[1].to_sym
               node.load_attribute_by_short_filename(amatch[2], cookbook_name)
             else
               cookbook_name = fully_qualified_attribute_short_filename.to_sym
               node.load_attribute_by_short_filename("default", cookbook_name)
             end
           end
           true
         end
   
       end
     end
   end
         
   

lib/chef/file_access_control.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   
   class Chef
   
     # == Chef::FileAccessControl
     # FileAccessControl objects set the owner, group and mode of +file+ to
     # the values specified by a value object, usually a Chef::Resource.
     class FileAccessControl
       UINT = (1 << 32)
       UID_MAX = (1 << 32) - 10
     
       attr_reader :resource
     
       attr_reader :file
     
       # FileAccessControl objects set the owner, group and mode of +file+ to
       # the values specified by +resource+. +file+ is completely independent
       # of any file or path attribute on +resource+, so it is possible to set
       # access control settings on a tempfile (for example).
       # === Arguments:
       # resource:   probably a Chef::Resource::File object (or subclass), but
       #             this is not required. Must respond to +owner+, +group+,
       #             and +mode+
       # file:       The file whose access control settings you wish to modify,
       #             given as a String.
       def initialize(resource, file)
         @resource, @file = resource, file
         @modified = false
       end
     
       def modified?
         @modified
       end
     
       def set_all
         set_owner
         set_group
         set_mode
       end
     
       # Workaround the fact that Ruby's Etc module doesn't believe in negative
       # uids, so negative uids show up as the diminished radix complement of
       # a uint. For example, a uid of -2 is reported as 4294967294
       def diminished_radix_complement(int)
         if int > UID_MAX
           int - UINT
         else
           int
         end
       end
     
       def target_uid
         return nil if resource.owner.nil?
         if resource.owner.kind_of?(String)
           diminished_radix_complement( Etc.getpwnam(resource.owner).uid )
         elsif resource.owner.kind_of?(Integer)
           resource.owner
         else
           Chef::Log.error("The `owner` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
           raise ArgumentError, "cannot resolve #{resource.owner.inspect} to uid, owner must be a string or integer"
         end
       rescue ArgumentError
         raise Chef::Exceptions::UserIDNotFound, "cannot determine user id for '#{resource.owner}', does the user exist on this system?"
       end
     
       def set_owner
         if (uid = target_uid) && (uid != stat.uid)
           File.chown(uid, nil, file)
           Chef::Log.info("#{log_string} owner changed to #{uid}")
           modified
         end
       end
     
       def target_gid
         return nil if resource.group.nil?
         if resource.group.kind_of?(String)
           diminished_radix_complement( Etc.getgrnam(resource.group).gid )
         elsif resource.group.kind_of?(Integer)
           resource.group
         else
           Chef::Log.error("The `group` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
           raise ArgumentError, "cannot resolve #{resource.group.inspect} to gid, group must be a string or integer"
         end
       rescue ArgumentError
         raise Chef::Exceptions::GroupIDNotFound, "cannot determine group id for '#{resource.group}', does the group exist on this system?"
       end
     
       def set_group
         if (gid = target_gid) && (gid != stat.gid)
           File.chown(nil, gid, file)
           Chef::Log.info("#{log_string} group changed to #{gid}")
           modified
         end
       end
   
       def target_mode
         return nil if resource.mode.nil?
         (resource.mode.respond_to?(:oct) ? resource.mode.oct : resource.mode.to_i) & 007777
       end
   
       def set_mode
         if (mode = target_mode) && (mode != (stat.mode & 007777))
           File.chmod(target_mode, file)
           Chef::Log.info("#{log_string} mode changed to #{mode.to_s(8)}")
           modified
         end
       end
     
   
       def stat
         @stat ||= ::File.stat(file)
       end
     
       private
     
       def modified
         @modified = true
       end
   
       def log_string
         @resource || @file
       end
     
     end
   end

lib/chef/mixin/language.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/search/query'
   require 'chef/data_bag'
   require 'chef/data_bag_item'
   require 'chef/encrypted_data_bag_item'
   
   class Chef
     module Mixin
       module Language
   
         # Implementation class for determining platform dependent values
         class PlatformDependentValue
   
           # Create a platform dependent value object.
           # === Arguments
           # platform_hash (Hash) a hash of the same structure as Chef::Platform,
           # like this:
           #   {
           #     :debian => {:default => 'the value for all debian'}
           #     [:centos, :redhat, :fedora] => {:default => "value for all EL variants"}
           #     :ubuntu => { :default => "default for ubuntu", '10.04' => "value for 10.04 only"},
           #     :default => "the default when nothing else matches"
           #   }
           # * platforms can be specified as Symbols or Strings
           # * multiple platforms can be grouped by using an Array as the key
           # * values for platforms need to be Hashes of the form:
           #   {platform_version => value_for_that_version}
           # * the exception to the above is the default value, which is given as
           #   :default => default_value
           def initialize(platform_hash)
             @values = {}
             platform_hash.each { |platforms, value| set(platforms, value)}
           end
   
           def value_for_node(node)
             platform, version = node[:platform].to_s, node[:platform_version].to_s
             if @values.key?(platform) && @values[platform].key?(version)
               @values[platform][version]
             elsif @values.key?(platform) && @values[platform].key?("default")
               @values[platform]["default"]
             elsif @values.key?("default")
               @values["default"]
             else
               nil
             end
           end
   
           private
   
           def set(platforms, value)
             if platforms.to_s == 'default'
               @values["default"] = value
             else
               assert_valid_platform_values!(platforms, value)
               Array(platforms).each { |platform| @values[platform.to_s] = normalize_keys(value)}
               value
             end
           end
   
           def normalize_keys(hash)
             hash.inject({}) do |h, key_value|
               keys, value = *key_value
               Array(keys).each do |key|
                 h[key.to_s] = value
               end
               h
             end
           end
   
           def assert_valid_platform_values!(platforms, value)
             unless value.kind_of?(Hash)
               msg = "platform dependent values must be specified in the format :platform => {:version => value} "
               msg << "you gave a value #{value.inspect} for platform(s) #{platforms}"
               raise ArgumentError, msg
             end
           end
         end
   
         # Given a hash similar to the one we use for Platforms, select a value from the hash.  Supports
         # per platform defaults, along with a single base default. Arrays may be passed as hash keys and
         # will be expanded.
         #
         # === Parameters
         # platform_hash:: A platform-style hash.
         #
         # === Returns
         # value:: Whatever the most specific value of the hash is.
         def value_for_platform(platform_hash)
           PlatformDependentValue.new(platform_hash).value_for_node(node)
         end
   
         # Given a list of platforms, returns true if the current recipe is being run on a node with
         # that platform, false otherwise.
         #
         # === Parameters
         # args:: A list of platforms
         #
         # === Returns
         # true:: If the current platform is in the list
         # false:: If the current platform is not in the list
         def platform?(*args)
           has_platform = false
   
           args.flatten.each do |platform|
             has_platform = true if platform == node[:platform]
           end
   
           has_platform
         end
   
         def search(*args, &block)
           # If you pass a block, or have at least the start argument, do raw result parsing
           #
           # Otherwise, do the iteration for the end user
           if Kernel.block_given? || args.length >= 4
             Chef::Search::Query.new.search(*args, &block)
           else
             results = Array.new
             Chef::Search::Query.new.search(*args) do |o|
               results << o
             end
             results
           end
         end
   
         def data_bag(bag)
           DataBag.validate_name!(bag.to_s)
           rbag = DataBag.load(bag)
           rbag.keys
         rescue Exception
           Log.error("Failed to list data bag items in data bag: #{bag.inspect}")
           raise
         end
   
         def data_bag_item(bag, item)
           DataBag.validate_name!(bag.to_s)
           DataBagItem.validate_id!(item)
           DataBagItem.load(bag, item)
         rescue Exception
           Log.error("Failed to load data bag item: #{bag.inspect} #{item.inspect}")
           raise
         end
   
       end
     end
   end

lib/chef/api_client.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/from_file'
   require 'chef/couchdb'
   require 'chef/certificate'
   require 'chef/index_queue'
   require 'chef/mash'
   require 'chef/json_compat'
   require 'chef/search/query'
   
   class Chef
     class ApiClient
   
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       include Chef::IndexQueue::Indexable
   
   
       DESIGN_DOCUMENT = {
         "version" => 1,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "client") {
                 emit(doc.name, doc);
               }
             }
             EOJS
           },
           "all_id" => {
             "map" => <<-EOJS
             function(doc) {
               if (doc.chef_type == "client") {
                 emit(doc.name, doc.name);
               }
             }
             EOJS
           }
         }
       }
   
       INDEX_OBJECT_TYPE = 'client'.freeze
   
       def self.index_object_type
         INDEX_OBJECT_TYPE
       end
   
       attr_accessor :couchdb_rev, :couchdb_id, :couchdb
   
       # Create a new Chef::ApiClient object.
       def initialize(couchdb=nil)
         @name = ''
         @public_key = nil
         @private_key = nil
         @couchdb_rev = nil
         @couchdb_id = nil
         @admin = false
         @couchdb = (couchdb || Chef::CouchDB.new)
       end
   
       # Gets or sets the client name.
       #
       # @params [Optional String] The name must be alpha-numeric plus - and _.
       # @return [String] The current value of the name.
       def name(arg=nil)
         set_or_return(
           :name,
           arg,
           :regex => /^[\-[:alnum:]_\.]+$/
         )
       end
   
       # Gets or sets whether this client is an admin.
       #
       # @params [Optional True/False] Should be true or false - default is false.
       # @return [True/False] The current value
       def admin(arg=nil)
         set_or_return(
           :admin,
           arg,
           :kind_of => [ TrueClass, FalseClass ]
         )
       end
   
       # Gets or sets the public key.
       #
       # @params [Optional String] The string representation of the public key.
       # @return [String] The current value.
       def public_key(arg=nil)
         set_or_return(
           :public_key,
           arg,
           :kind_of => String
         )
       end
   
       # Gets or sets the private key.
       #
       # @params [Optional String] The string representation of the private key.
       # @return [String] The current value.
       def private_key(arg=nil)
         set_or_return(
           :private_key,
           arg,
           :kind_of => String
         )
       end
   
       # Creates a new public/private key pair, and populates the public_key and
       # private_key attributes.
       #
       # @return [True]
       def create_keys
         results = Chef::Certificate.gen_keypair(self.name)
         self.public_key(results[0].to_s)
         self.private_key(results[1].to_s)
         true
       end
   
       # The hash representation of the object.  Includes the name and public_key,
       # but never the private key.
       #
       # @return [Hash]
       def to_hash
         result = {
           "name" => @name,
           "public_key" => @public_key,
           "admin" => @admin,
           'json_class' => self.class.name,
           "chef_type" => "client"
         }
         result["_rev"] = @couchdb_rev if @couchdb_rev
         result
       end
   
       # The JSON representation of the object.
       #
       # @return [String] the JSON string.
       def to_json(*a)
         to_hash.to_json(*a)
       end
   
       def self.json_create(o)
         client = Chef::ApiClient.new
         client.name(o["name"] || o["clientname"])
         client.public_key(o["public_key"])
         client.admin(o["admin"])
         client.couchdb_rev = o["_rev"]
         client.couchdb_id = o["_id"]
         client.index_id = client.couchdb_id
         client
       end
   
       # List all the Chef::ApiClient objects in the CouchDB.  If inflate is set
       # to true, you will get the full list of all ApiClients, fully inflated.
       def self.cdb_list(inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).list("clients", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }
       end
   
       def self.list(inflate=false)
         if inflate
           response = Hash.new
           Chef::Search::Query.new.search(:client) do |n|
             n = self.json_create(n) if n.instance_of?(Hash)
             response[n.name] = n
           end
           response
         else
           Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("clients")
         end
       end
   
       # Load a client by name from CouchDB
       #
       # @params [String] The name of the client to load
       # @return [Chef::ApiClient] The resulting Chef::ApiClient object
       def self.cdb_load(name, couchdb=nil)
         (couchdb || Chef::CouchDB.new).load("client", name)
       end
   
       # Load a client by name via the API
       def self.load(name)
         response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("clients/#{name}")
         if response.kind_of?(Chef::ApiClient)
           response
         else
           client = Chef::ApiClient.new
           client.name(response['clientname'])
           client
         end
       end
   
       # Remove this client from the CouchDB
       #
       # @params [String] The name of the client to delete
       # @return [Chef::ApiClient] The last version of the object
       def cdb_destroy
         @couchdb.delete("client", @name, @couchdb_rev)
       end
   
       # Remove this client via the REST API
       def destroy
         Chef::REST.new(Chef::Config[:chef_server_url]).delete_rest("clients/#{@name}")
       end
   
       # Save this client to the CouchDB
       def cdb_save
         @couchdb_rev = @couchdb.store("client", @name, self)["rev"]
       end
   
       # Save this client via the REST API, returns a hash including the private key
       def save(new_key=false, validation=false)
         if validation
           r = Chef::REST.new(Chef::Config[:chef_server_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key])
         else
           r = Chef::REST.new(Chef::Config[:chef_server_url])
         end
         # First, try and create a new registration
         begin
           r.post_rest("clients", {:name => self.name, :admin => self.admin })
         rescue Net::HTTPServerException => e
           # If that fails, go ahead and try and update it
           if e.response.code == "409"
             r.put_rest("clients/#{name}", { :name => self.name, :admin => self.admin, :private_key => new_key })
           else
             raise e
           end
         end
       end
   
       # Create the client via the REST API
       def create
         Chef::REST.new(Chef::Config[:chef_server_url]).post_rest("clients", self)
       end
   
       # Set up our CouchDB design document
       def self.create_design_document(couchdb=nil)
         (couchdb ||= Chef::CouchDB.new).create_design_document("clients", DESIGN_DOCUMENT)
       end
   
       # As a string
       def to_s
         "client[#{@name}]"
       end
   
     end
   end
   

lib/chef/provider/service/gentoo.rb

   #
   # Author:: Lee Jensen ()
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/mixin/command'
   
   class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
     def load_current_resource
   
       @new_resource.supports[:status] = true
       @new_resource.supports[:restart] = true
   
       super
       
       raise Chef::Exceptions::Service unless ::File.exists?("/sbin/rc-update")
       
       @current_resource.enabled(
         Dir.glob("/etc/runlevels/**/#{@current_resource.service_name}").any? do |file|
           exists = ::File.exists? file
           readable = ::File.readable? file
           Chef::Log.debug "#{@new_resource} exists: #{exists}, readable: #{readable}"
           exists and readable
         end
       )
       Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}"
   
       @current_resource
     end
     
     def enable_service()
       run_command(:command => "/sbin/rc-update add #{@new_resource.service_name} default")
     end
     
     def disable_service()
       run_command(:command => "/sbin/rc-update del #{@new_resource.service_name} default")
     end
   end

lib/chef/knife/core/node_editor.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/json_compat'
   require 'chef/node'
   
   class Chef
     class Knife
       class NodeEditor
   
         attr_reader :node
         attr_reader :ui
         attr_reader :config
   
         def initialize(node, ui, config)
           @node, @ui, @config = node, ui, config
         end
   
         def edit_node
           abort "You specified the --no-editor option, nothing to edit" if config[:no_editor]
           assert_editor_set!
   
           updated_node_data = edit_data(view)
           apply_updates(updated_node_data)
           @updated_node
         end
   
         def view
           result = {}
           result["name"] = node.name
           result["chef_environment"] = node.chef_environment
           result["normal"] = node.normal_attrs
           result["run_list"] = node.run_list
   
           if config[:all_attributes]
             result["default"]   = node.default_attrs
             result["override"]  = node.override_attrs
             result["automatic"] = node.automatic_attrs
           end
           Chef::JSONCompat.to_json_pretty(result)
         end
   
         def edit_data(text)
           edited_data = tempfile_for(text) {|filename| system("#{config[:editor]} #{filename}")}
           Chef::JSONCompat.from_json(edited_data)
         end
   
         def apply_updates(updated_data)
           # TODO: should warn/error/ask for confirmation when changing the
           # name, since this results in a new node, not an edited node.
           @updated_node = Node.new.tap do |n|
             n.name( updated_data["name"] )
             n.chef_environment( updated_data["chef_environment"] )
             n.run_list( updated_data["run_list"])
             n.normal_attrs = updated_data["normal"]
   
             if config[:all_attributes]
               n.default_attrs   = updated_data["default"]
               n.override_attrs  = updated_data["override"]
               n.automatic_attrs = updated_data["automatic"]
             else
               n.default_attrs   = node.default_attrs
               n.override_attrs  = node.override_attrs
               n.automatic_attrs = node.automatic_attrs
             end
           end
         end
   
         def updated?
           pristine_copy = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(node), :create_additions => false)
           updated_copy  = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@updated_node), :create_additions => false)
           unless pristine_copy == updated_copy
             updated_properties = %w{name normal chef_environment run_list default override automatic}.reject do |key|
                pristine_copy[key] == updated_copy[key]
             end
           end
           ( pristine_copy != updated_copy ) && updated_properties
         end
   
         private
   
         def abort(message)
           ui.error(message)
           exit 1
         end
   
         def assert_editor_set!
           unless config[:editor]
             abort "You must set your EDITOR environment variable or configure your editor via knife.rb"
           end
         end
   
         def tempfile_for(data)
           # TODO: include useful info like the node name in the temp file
           # name
           basename = "knife-edit-" << rand(1_000_000_000_000_000).to_s.rjust(15, '0') << '.js'
           filename = File.join(Dir.tmpdir, basename)
           File.open(filename, "w+") do |f|
             f.sync = true
             f.puts data
           end
   
           yield filename
   
           IO.read(filename)
         ensure
           File.unlink(filename)
         end
       end
     end
   end
   

lib/chef/mixin/create_path.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     module Mixin
       module CreatePath
         
         # Creates a given path, including all directories that lead up to it.
         # Like mkdir_p, but without the leaking.
         #
         # === Parameters
         # file_path:: A string that represents the path to create, 
         #   or an Array with the path-parts.
         #
         # === Returns
         # The created file_path.
         def create_path(file_path)
           unless file_path.kind_of?(String) || file_path.kind_of?(Array)
             raise ArgumentError, "file_path must be a string or an array!" 
           end
           
           if file_path.kind_of?(String)
             file_path = File.expand_path(file_path).split(File::SEPARATOR)
             file_path.shift if file_path[0] == ''
             # Check if path starts with a separator or drive letter (Windows)
             unless file_path[0].match("^#{File::SEPARATOR}|^[a-zA-Z]:")
               file_path[0] = "#{File::SEPARATOR}#{file_path[0]}"
             end
           end
                   
           file_path.each_index do |i|
             create_path = File.join(file_path[0, i + 1])
             unless File.directory?(create_path)
               Chef::Log.debug("Creating directory #{create_path}")
               Dir.mkdir(create_path)
             end 
           end
           File.expand_path(File.join(file_path))
         end
     
       end
     end
   end

lib/chef/resource/easy_install_package.rb

   #
   # Author:: Joe Williams ()
   # Copyright:: Copyright (c) 2009 Joe Williams
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/package'
   
   class Chef
     class Resource
       class EasyInstallPackage < Chef::Resource::Package
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :easy_install_package
           @provider = Chef::Provider::Package::EasyInstall
         end
   
         def easy_install_binary(arg=nil)
           set_or_return(
             :easy_install_binary,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def python_binary(arg=nil)
           set_or_return(
             :python_install_binary,
             arg,
             :kind_of => [ String ]
           )
         end
   
         def module_name(arg=nil)
           set_or_return(
             :module_name,
             arg,
             :kind_of => [ String ]
           )
         end
   
       end
     end
   end

lib/chef/knife/node_run_list_add.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeRunListAdd < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node run_list add [NODE] [ENTRY] (options)"
   
         option :after,
           :short => "-a ITEM",
           :long  => "--after ITEM",
           :description => "Place the ENTRY in the run list after ITEM"
   
         def run
           node = Chef::Node.load(@name_args[0])
           entry = @name_args[1]
   
           add_to_run_list(node, entry, config[:after])
   
           node.save
   
           config[:run_list] = true
   
           output(format_for_display(node))
         end
   
         def add_to_run_list(node, new_value, after=nil)
           if after
             nlist = []
             node.run_list.each do |entry|
               nlist << entry
               if entry == after
                 nlist << new_value
               end
             end
             node.run_list.reset!(nlist)
           else
             node.run_list << new_value
           end
         end
   
       end
     end
   end

lib/chef/knife/data_bag_edit.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class DataBagEdit < Knife
   
         deps do
           require 'chef/data_bag_item'
           require 'chef/encrypted_data_bag_item'
         end
   
         banner "knife data bag edit BAG ITEM (options)"
         category "data bag"
   
         option :secret,
         :short => "-s SECRET",
         :long  => "--secret ",
         :description => "The secret key to use to encrypt data bag item values"
   
         option :secret_file,
         :long => "--secret-file SECRET_FILE",
         :description => "A file containing the secret key to use to encrypt data bag item values"
   
         def read_secret
           if config[:secret]
             config[:secret]
           else
             Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
           end
         end
   
         def use_encryption
           if config[:secret] && config[:secret_file]
             stdout.puts "please specify only one of --secret, --secret-file"
             exit(1)
           end
           config[:secret] || config[:secret_file]
         end
   
         def load_item(bag, item_name)
           item = Chef::DataBagItem.load(bag, item_name)
           if use_encryption
             Chef::EncryptedDataBagItem.new(item, read_secret).to_hash
           else
             item
           end
         end
   
         def edit_item(item)
           output = edit_data(item)
           if use_encryption
             Chef::EncryptedDataBagItem.encrypt_data_bag_item(output, read_secret)
           else
             output
           end
         end
   
         def run
           if @name_args.length != 2
             stdout.puts "You must supply the data bag and an item to edit!"
             stdout.puts opt_parser
             exit 1
           end
           item = load_item(@name_args[0], @name_args[1])
           output = edit_item(item)
           rest.put_rest("data/#{@name_args[0]}/#{@name_args[1]}", output)
           stdout.puts("Saved data_bag_item[#{@name_args[1]}]")
           output(format_for_display(object.raw_data)) if config[:print_after]
         end
       end
     end
   end
   
   
   

lib/chef/version_class.rb

   # Author:: Seth Falcon ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     class Version
       include Comparable
       attr_reader :major, :minor, :patch
   
       def initialize(str="")
         @major, @minor, @patch = parse(str)
       end
   
       def inspect
         "#{@major}.#{@minor}.#{@patch}"
       end
   
       def to_s
         "#{@major}.#{@minor}.#{@patch}"
       end
   
       def <=>(v)
         [:major, :minor, :patch].each do |method|
           ans = (self.send(method) <=> v.send(method))
           return ans if ans != 0
         end
         0
       end
   
       def hash
         # Didn't put any thought or research into this, probably can be
         # done better
         to_s.hash
       end
   
       # For hash
       def eql?(other)
         other.is_a?(Version) && self == other
       end
   
       private
   
       def parse(str="")
         @major, @minor, @patch =
           case str.to_s
           when /^(\d+)\.(\d+)\.(\d+)$/
             [ $1.to_i, $2.to_i, $3.to_i ]
           when /^(\d+)\.(\d+)$/
             [ $1.to_i, $2.to_i, 0 ]
           else
             msg = "'#{str.to_s}' does not match 'x.y.z' or 'x.y'"
             raise Chef::Exceptions::InvalidCookbookVersion.new( msg )
           end
       end
   
     end
   end

lib/chef/mixin/language_include_recipe.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/log'
   
   class Chef
     module Mixin
       module LanguageIncludeRecipe
   
         def include_recipe(*recipe_names)
           result_recipes = Array.new
           recipe_names.flatten.each do |recipe_name|
             if node.run_state[:seen_recipes].has_key?(recipe_name)
               Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.")
               next
             end
   
             Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe")
             node.run_state[:seen_recipes][recipe_name] = true
   
             cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name)
   
             run_context = self.is_a?(Chef::RunContext) ? self : self.run_context
             cookbook = run_context.cookbook_collection[cookbook_name]
             result_recipes << cookbook.load_recipe(recipe_short_name, run_context)
           end
           result_recipes
         end
   
         def require_recipe(*args)
           include_recipe(*args)
         end
   
       end
     end
   end
         

lib/chef/knife/search.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   require 'chef/knife/core/node_presenter'
   
   class Chef
     class Knife
       class Search < Knife
   
         deps do
           require 'chef/node'
           require 'chef/environment'
           require 'chef/api_client'
           require 'chef/search/query'
         end
   
         include Knife::Core::NodeFormattingOptions
   
         banner "knife search INDEX QUERY (options)"
   
         option :sort,
           :short => "-o SORT",
           :long => "--sort SORT",
           :description => "The order to sort the results in",
           :default => nil
   
         option :start,
           :short => "-b ROW",
           :long => "--start ROW",
           :description => "The row to start returning results at",
           :default => 0,
           :proc => lambda { |i| i.to_i }
   
         option :rows,
           :short => "-R INT",
           :long => "--rows INT",
           :description => "The number of rows to return",
           :default => 1000,
           :proc => lambda { |i| i.to_i }
   
         option :attribute,
           :short => "-a ATTR",
           :long => "--attribute ATTR",
           :description => "Show only one attribute"
   
         option :run_list,
           :short => "-r",
           :long => "--run-list",
           :description => "Show only the run list"
   
         option :id_only,
           :short => "-i",
           :long => "--id-only",
           :description => "Show only the ID of matching objects"
   
         option :query,
           :short => "-q QUERY",
           :long => "--query QUERY",
           :description => "The search query; useful to protect queries starting with -"
   
         def run
           if config[:query] && @name_args[1]
             ui.error "please specify query as an argument or an option via -q, not both"
             ui.msg opt_parser
             exit 1
           end
           raw_query = config[:query] || @name_args[1]
           if !raw_query || raw_query.empty?
             ui.error "no query specified"
             ui.msg opt_parser
             exit 1
           end
   
           if name_args[0].nil?
             ui.error "you must specify an item type to search for"
             exit 1
           end
   
           if name_args[0] == 'node'
             ui.use_presenter Knife::Core::NodePresenter
           end
   
   
           q = Chef::Search::Query.new
           query = URI.escape(raw_query,
                              Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
   
           result_items = []
           result_count = 0
   
           rows = config[:rows]
           start = config[:start]
           begin
             q.search(@name_args[0], query, config[:sort], start, rows) do |item|
               formatted_item = format_for_display(item)
               if formatted_item.respond_to?(:has_key?) && !formatted_item.has_key?('id')
                 formatted_item['id'] = item.has_key?('id') ? item['id'] : item.name
               end
               result_items << formatted_item
               result_count += 1
             end
           rescue Net::HTTPServerException => e
             msg = Chef::JSONCompat.from_json(e.response.body)["error"].first
             ui.error("knife search failed: #{msg}")
             exit 1
           end
   
           if ui.interchange?
             output({:results => result_count, :rows => result_items})
           else
             ui.msg "#{result_count} items found"
             ui.msg("\n")
             result_items.each do |item|
               output(item)
               ui.msg("\n")
             end
           end
         end
       end
     end
   end
   
   
   
   

lib/chef/encrypted_data_bag_item.rb

   #
   # Author:: Seth Falcon ()
   # Copyright:: Copyright 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'base64'
   require 'openssl'
   require 'chef/data_bag_item'
   require 'yaml'
   require 'open-uri'
   
   # An EncryptedDataBagItem represents a read-only data bag item where
   # all values, except for the value associated with the id key, have
   # been encrypted.
   #
   # EncrypedDataBagItem can be used in recipes to decrypt data bag item
   # members.
   #
   # Data bag item values are assumed to have been encrypted using the
   # default symmetric encryption provided by Encryptor.encrypt where
   # values are converted to YAML prior to encryption.
   #
   # If the shared secret is not specified at initialization or load,
   # then the contents of the file referred to in
   # Chef::Config[:encrypted_data_bag_secret] will be used as the
   # secret.  The default path is /etc/chef/encrypted_data_bag_secret
   #
   # EncryptedDataBagItem is intended to provide a means to avoid storing
   # data bag items in the clear on the Chef server.  This provides some
   # protection against a breach of the Chef server or of Chef server
   # backup data.  Because the secret must be stored in the clear on any
   # node needing access to an EncryptedDataBagItem, this approach
   # provides no protection of data bag items from actors with access to
   # such nodes in the infrastructure.
   #
   class Chef::EncryptedDataBagItem
     DEFAULT_SECRET_FILE = "/etc/chef/encrypted_data_bag_secret"
     ALGORITHM = 'aes-256-cbc'
   
     def initialize(enc_hash, secret)
       @enc_hash = enc_hash
       @secret = secret
     end
   
     def [](key)
       value = @enc_hash[key]
       if key == "id" || value.nil?
         value
       else
         self.class.decrypt_value(value, @secret)
       end
     end
   
     def []=(key, value)
       raise ArgumentError, "assignment not supported for #{self.class}"
     end
   
     def to_hash
       @enc_hash.keys.inject({}) { |hash, key| hash[key] = self[key]; hash }
     end
   
     def self.from_plain_hash(plain_hash, secret)
       self.new(self.encrypt_data_bag_item(plain_hash, secret), secret)
     end
   
     def self.encrypt_data_bag_item(plain_hash, secret)
       plain_hash.inject({}) do |h, (key, val)|
         h[key] = if key != "id"
                    self.encrypt_value(val, secret)
                  else
                    val
                  end
         h
       end
     end
   
     def self.load(data_bag, name, secret = nil)
       path = "data/#{data_bag}/#{name}"
       raw_hash = Chef::DataBagItem.load(data_bag, name)
       secret = secret || self.load_secret
       self.new(raw_hash, secret)
     end
   
     def self.encrypt_value(value, key)
       Base64.encode64(self.cipher(:encrypt, value.to_yaml, key))
     end
   
     def self.decrypt_value(value, key)
       YAML.load(self.cipher(:decrypt, Base64.decode64(value), key))
     end
   
     def self.load_secret(path=nil)
       path = path || Chef::Config[:encrypted_data_bag_secret] || DEFAULT_SECRET_FILE
       secret = case path
                when /^\w+:\/\//
                  # We have a remote key
                  begin
                    Kernel.open(path).read.strip
                  rescue Errno::ECONNREFUSED
                    raise ArgumentError, "Remote key not available from '#{path}'"
                  rescue OpenURI::HTTPError
                    raise ArgumentError, "Remote key not found at '#{path}'"
                  end
                else
                  if !File.exists?(path)
                    raise Errno::ENOENT, "file not found '#{path}'"
                  end
                  IO.read(path).strip
                end
       if secret.size < 1
         raise ArgumentError, "invalid zero length secret in '#{path}'"
       end
       secret
     end
   
     protected
   
     def self.cipher(direction, data, key)
       cipher = OpenSSL::Cipher::Cipher.new(ALGORITHM)
       cipher.send(direction)
       cipher.pkcs5_keyivgen(key)
       ans = cipher.update(data)
       ans << cipher.final
       ans
     end
   end

lib/chef/knife/cookbook_site_unshare.rb

   #
   # Author:: Stephen Delano ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteUnshare < Knife
   
         deps do
           require 'chef/json_compat'
         end
   
         banner "knife cookbook site unshare COOKBOOK"
         category "cookbook site"
   
         def run
           @cookbook_name = @name_args[0]
           if @cookbook_name.nil?
             show_usage
             ui.fatal "You must provide the name of the cookbook to unshare"
             exit 1
           end
   
           confirm "Do you really want to unshare the cookbook #{@cookbook_name}"
   
           begin
             rest.delete_rest "http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}"
           rescue Net::HTTPServerException => e
             raise e unless e.message =~ /Forbidden/
             ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it."
             exit 1
           end
   
           ui.info "Unshared cookbook #{@cookbook_name}"
         end
   
       end
     end
   end

lib/chef/rest/auth_credentials.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Thom May ()
   # Author:: Nuo Yan ()
   # Author:: Christopher Brown ()
   # Author:: Christopher Walters ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'chef/exceptions'
   require 'mixlib/authentication/signedheaderauth'
   
   class Chef
     class REST
       class AuthCredentials
         attr_reader :key_file, :client_name, :key, :raw_key
   
         def initialize(client_name=nil, key_file=nil)
           @client_name, @key_file = client_name, key_file
           load_signing_key if sign_requests?
         end
   
         def sign_requests?
             key_file
         end
   
         def signature_headers(request_params={})
           raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if client_name.nil?
           Chef::Log.debug("Signing the request as #{client_name}")
   
           # params_in = {:http_method => :GET, :path => "/clients", :body => "", :host => "localhost"}
           request_params             = request_params.dup
           request_params[:timestamp] = Time.now.utc.iso8601
           request_params[:user_id]   = client_name
           host = request_params.delete(:host) || "localhost"
   
           sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
           signed =  sign_obj.sign(key).merge({:host => host})
           signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
         end
   
         private
   
         def load_signing_key
           @raw_key = IO.read(key_file).strip
           @key = OpenSSL::PKey::RSA.new(@raw_key)
         rescue SystemCallError, IOError => e
           Chef::Log.warn "Failed to read the private key #{key_file}: #{e.inspect}"
           raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
         rescue OpenSSL::PKey::RSAError
           msg = "The file #{key_file} does not contain a correctly formatted private key.\n"
           msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
           raise Chef::Exceptions::InvalidPrivateKey, msg
         end
   
       end
     end
   end

lib/chef/shef/model_wrapper.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/convert_to_class_name'
   require 'chef/mixin/language'
   
   module Shef
     class ModelWrapper
   
       include Chef::Mixin::ConvertToClassName
   
       attr_reader :model_symbol
   
       def initialize(model_class, symbol=nil)
         @model_class = model_class
         @model_symbol = symbol || convert_to_snake_case(model_class.name, "Chef").to_sym
       end
   
       def search(query)
         return all if query.to_s == "all"
         results = []
         Chef::Search::Query.new.search(@model_symbol, format_query(query)) do |obj|
           if block_given?
             results << yield(obj)
           else
             results << obj
           end
         end
         results
       end
   
       alias :find :search
   
       def all(&block)
         all_objects = list_objects
         block_given? ? all_objects.map(&block) : all_objects
       end
   
       alias :list :all
   
       def show(obj_id)
         @model_class.load(obj_id)
       end
   
       alias :load :show
   
       def transform(what_to_transform, &block)
         if what_to_transform == :all
           objects_to_transform = list_objects
         else
           objects_to_transform = search(what_to_transform)
         end
         objects_to_transform.each do |obj|
           if result = yield(obj)
             obj.save
           end
         end
       end
   
       alias :bulk_edit :transform
   
       private
   
       # paper over inconsistencies in the model classes APIs, and return the objects
       # the user wanted instead of the URI=>object stuff
       def list_objects
         objects = @model_class.method(:list).arity == 0? @model_class.list : @model_class.list(true)
         objects.map { |obj| Array(obj).find {|o| o.kind_of?(@model_class)} }
       end
   
       def format_query(query)
         if query.respond_to?(:keys)
           query.map { |key, value| "#{key}:#{value}" }.join(" AND ")
         else
           query
         end
       end
     end
   
     class NamedDataBagWrapper < ModelWrapper
   
       def initialize(databag_name)
         @model_symbol = @databag_name = databag_name
       end
   
   
       alias :list :all
   
       def show(item)
         Chef::DataBagItem.load(@databag_name, item)
       end
   
       private
   
       def list_objects
         all_items = []
         Chef::Search::Query.new.search(@databag_name) do |item|
           all_items << item
         end
         all_items
       end
   
     end
   
   end

lib/chef/handler/json_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/handler'
   require 'chef/resource/directory'
   
   class Chef
     class Handler
       class JsonFile < ::Chef::Handler
   
         attr_reader :config
   
         def initialize(config={})
           @config = config
           @config[:path] ||= "/var/chef/reports"
           @config
         end
   
         def report
           if exception
             Chef::Log.error("Creating JSON exception report")
           else
             Chef::Log.info("Creating JSON run report")
           end
   
           build_report_dir
           savetime = Time.now.strftime("%Y%m%d%H%M%S")
           File.open(File.join(config[:path], "chef-run-report-#{savetime}.json"), "w") do |file|
             file.puts Chef::JSONCompat.to_json_pretty(data)
           end
         end
   
         def build_report_dir
           unless File.exists?(config[:path])
             FileUtils.mkdir_p(config[:path])
             File.chmod(00700, config[:path])
           end
         end
   
   
       end
     end
   end

lib/chef/cookbook/metadata.rb

   #
   # Author:: Adam Jacob ()
   # Author:: AJ Christensen ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright 2008-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mash'
   require 'chef/mixin/from_file'
   require 'chef/mixin/params_validate'
   require 'chef/mixin/check_helper'
   require 'chef/log'
   require 'chef/version_class'
   require 'chef/version_constraint'
   
   class Chef
     class Cookbook
   
       # == Chef::Cookbook::Metadata
       # Chef::Cookbook::Metadata provides a convenient DSL for declaring metadata
       # about Chef Cookbooks.
       class Metadata
   
         NAME              = 'name'.freeze
         DESCRIPTION       = 'description'.freeze
         LONG_DESCRIPTION  = 'long_description'.freeze
         MAINTAINER        = 'maintainer'.freeze
         MAINTAINER_EMAIL  = 'maintainer_email'.freeze
         LICENSE           = 'license'.freeze
         PLATFORMS         = 'platforms'.freeze
         DEPENDENCIES      = 'dependencies'.freeze
         RECOMMENDATIONS   = 'recommendations'.freeze
         SUGGESTIONS       = 'suggestions'.freeze
         CONFLICTING       = 'conflicting'.freeze
         PROVIDING         = 'providing'.freeze
         REPLACING         = 'replacing'.freeze
         ATTRIBUTES        = 'attributes'.freeze
         GROUPINGS         = 'groupings'.freeze
         RECIPES           = 'recipes'.freeze
         VERSION           = 'version'.freeze
   
         COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer,
                               :maintainer_email, :license, :platforms, :dependencies,
                               :recommendations, :suggestions, :conflicting, :providing,
                               :replacing, :attributes, :groupings, :recipes, :version]
   
         VERSION_CONSTRAINTS = {:depends     => DEPENDENCIES,
                                :recommends  => RECOMMENDATIONS,
                                :suggests    => SUGGESTIONS,
                                :conflicts   => CONFLICTING,
                                :provides    => PROVIDING,
                                :replaces    => REPLACING }
   
         include Chef::Mixin::CheckHelper
         include Chef::Mixin::ParamsValidate
         include Chef::Mixin::FromFile
   
         attr_reader   :cookbook,
                       :platforms,
                       :dependencies,
                       :recommendations,
                       :suggestions,
                       :conflicting,
                       :providing,
                       :replacing,
                       :attributes,
                       :groupings,
                       :recipes,
                       :version
   
         # Builds a new Chef::Cookbook::Metadata object.
         #
         # === Parameters
         # cookbook:: An optional cookbook object
         # maintainer:: An optional maintainer
         # maintainer_email:: An optional maintainer email
         # license::An optional license. Default is Apache v2.0
         #
         # === Returns
         # metadata
         def initialize(cookbook=nil, maintainer='Your Name', maintainer_email='youremail@example.com', license='Apache v2.0')
           @cookbook = cookbook
           @name = cookbook ? cookbook.name : ""
           @long_description = ""
           self.maintainer(maintainer)
           self.maintainer_email(maintainer_email)
           self.license(license)
           self.description('A fabulous new cookbook')
           @platforms = Mash.new
           @dependencies = Mash.new
           @recommendations = Mash.new
           @suggestions = Mash.new
           @conflicting = Mash.new
           @providing = Mash.new
           @replacing = Mash.new
           @attributes = Mash.new
           @groupings = Mash.new
           @recipes = Mash.new
           @version = Version.new "0.0.0"
           if cookbook
             @recipes = cookbook.fully_qualified_recipe_names.inject({}) do |r, e|
               e = self.name if e =~ /::default$/
               r[e] = ""
               self.provides e
               r
             end
           end
         end
   
         def ==(other)
           COMPARISON_FIELDS.inject(true) do |equal_so_far, field|
             equal_so_far && other.respond_to?(field) && (other.send(field) == send(field))
           end
         end
   
         # Sets the cookbooks maintainer, or returns it.
         #
         # === Parameters
         # maintainer:: The maintainers name
         #
         # === Returns
         # maintainer:: Returns the current maintainer.
         def maintainer(arg=nil)
           set_or_return(
             :maintainer,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Sets the maintainers email address, or returns it.
         #
         # === Parameters
         # maintainer_email:: The maintainers email address
         #
         # === Returns
         # maintainer_email:: Returns the current maintainer email.
         def maintainer_email(arg=nil)
           set_or_return(
             :maintainer_email,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Sets the current license, or returns it.
         #
         # === Parameters
         # license:: The current license.
         #
         # === Returns
         # license:: Returns the current license
         def license(arg=nil)
           set_or_return(
             :license,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Sets the current description, or returns it. Should be short - one line only!
         #
         # === Parameters
         # description:: The new description
         #
         # === Returns
         # description:: Returns the description
         def description(arg=nil)
           set_or_return(
             :description,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Sets the current long description, or returns it. Might come from a README, say.
         #
         # === Parameters
         # long_description:: The new long description
         #
         # === Returns
         # long_description:: Returns the long description
         def long_description(arg=nil)
           set_or_return(
             :long_description,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Sets the current cookbook version, or returns it.  Can be two or three digits, seperated
         # by dots.  ie: '2.1', '1.5.4' or '0.9'.
         #
         # === Parameters
         # version:: The curent version, as a string
         #
         # === Returns
         # version:: Returns the current version
         def version(arg=nil)
           if arg
             @version = Chef::Version.new(arg)
           end
   
           @version.to_s
         end
   
         # Sets the name of the cookbook, or returns it.
         #
         # === Parameters
         # name:: The curent cookbook name.
         #
         # === Returns
         # name:: Returns the current cookbook name.
         def name(arg=nil)
           set_or_return(
             :name,
             arg,
             :kind_of => [ String ]
           )
         end
   
         # Adds a supported platform, with version checking strings.
         #
         # === Parameters
         # platform,:: The platform (like :ubuntu or :mac_os_x)
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has
         # the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def supports(platform, *version_args)
           version = new_args_format(:supports, platform, version_args)
           validate_version_constraint(:supports, platform, version)
           @platforms[platform] = version
           @platforms[platform]
         end
   
         # Adds a dependency on another cookbook, with version checking strings.
         #
         # === Parameters
         # cookbook:: The cookbook
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has
         # the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def depends(cookbook, *version_args)
           version = new_args_format(:depends, cookbook, version_args)
           validate_version_constraint(:depends, cookbook, version)
           @dependencies[cookbook] = version
           @dependencies[cookbook]
         end
   
         # Adds a recommendation for another cookbook, with version checking strings.
         #
         # === Parameters
         # cookbook:: The cookbook
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has
         # the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def recommends(cookbook, *version_args)
           version = new_args_format(:recommends, cookbook,  version_args)
           validate_version_constraint(:recommends, cookbook, version)
           @recommendations[cookbook] = version
           @recommendations[cookbook]
         end
   
         # Adds a suggestion for another cookbook, with version checking strings.
         #
         # === Parameters
         # cookbook:: The cookbook
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has the
         # formx.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def suggests(cookbook, *version_args)
           version = new_args_format(:suggests, cookbook, version_args)
           validate_version_constraint(:suggests, cookbook, version)
           @suggestions[cookbook] = version
           @suggestions[cookbook]
         end
   
         # Adds a conflict for another cookbook, with version checking strings.
         #
         # === Parameters
         # cookbook:: The cookbook
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has
         # the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def conflicts(cookbook, *version_args)
           version = new_args_format(:conflicts, cookbook, version_args)
           validate_version_constraint(:conflicts, cookbook, version)
           @conflicting[cookbook] = version
           @conflicting[cookbook]
         end
   
         # Adds a recipe, definition, or resource provided by this cookbook.
         #
         # Recipes are specified as normal
         # Definitions are followed by (), and can include :params for prototyping
         # Resources are the stringified version (service[apache2])
         #
         # === Parameters
         # recipe, definition, resource:: The thing we provide
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has
         # the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def provides(cookbook, *version_args)
           version = new_args_format(:provides, cookbook, version_args)
           validate_version_constraint(:provides, cookbook, version)
           @providing[cookbook] = version
           @providing[cookbook]
         end
   
         # Adds a cookbook that is replaced by this one, with version checking strings.
         #
         # === Parameters
         # cookbook:: The cookbook we replace
         # version:: A version constraint of the form "OP VERSION",
         # where OP is one of < <= = > >= ~> and VERSION has the form x.y.z or x.y.
         #
         # === Returns
         # versions:: Returns the list of versions for the platform
         def replaces(cookbook, *version_args)
           version = new_args_format(:replaces, cookbook, version_args)
           validate_version_constraint(:replaces, cookbook, version)
           @replacing[cookbook] = version
           @replacing[cookbook]
         end
   
         # Adds a description for a recipe.
         #
         # === Parameters
         # recipe:: The recipe
         # description:: The description of the recipe
         #
         # === Returns
         # description:: Returns the current description
         def recipe(name, description)
           @recipes[name] = description
         end
   
         # Adds an attribute )hat a user needs to configure for this cookbook. Takes
         # a name (with the / notation for a nested attribute), followed by any of
         # these options
         #
         #   display_name:: What a UI should show for this attribute
         #   description:: A hint as to what this attr is for
         #   choice:: An array of choices to present to the user.
         #   calculated:: If true, the default value is calculated by the recipe and cannot be displayed.
         #   type:: "string" or "array" - default is "string"  ("hash" is supported for backwards compatibility)
         #   required:: Whether this attr is 'required', 'recommended' or 'optional' - default 'optional' (true/false values also supported for backwards compatibility)
         #   recipes:: An array of recipes which need this attr set.
         #   default,,:: The default value
         #
         # === Parameters
         # name:: The name of the attribute ('foo', or 'apache2/log_dir')
         # options:: The description of the options
         #
         # === Returns
         # options:: Returns the current options hash
         def attribute(name, options)
           validate(
             options,
             {
               :display_name => { :kind_of => String },
               :description => { :kind_of => String },
               :choice => { :kind_of => [ Array ], :default => [] },
               :calculated => { :equal_to => [ true, false ], :default => false },
               :type => { :equal_to => [ "string", "array", "hash", "symbol" ], :default => "string" },
               :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" },
               :recipes => { :kind_of => [ Array ], :default => [] },
               :default => { :kind_of => [ String, Array, Hash ] }
             }
           )
           options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil?
           validate_string_array(options[:choice])
           validate_calculated_default_rule(options)
           validate_choice_default_rule(options)
   
           @attributes[name] = options
           @attributes[name]
         end
   
         def grouping(name, options)
           validate(
             options,
             {
               :title => { :kind_of => String },
               :description => { :kind_of => String }
             }
           )
           @groupings[name] = options
           @groupings[name]
         end
   
         def to_hash
           {
             NAME             => self.name,
             DESCRIPTION      => self.description,
             LONG_DESCRIPTION => self.long_description,
             MAINTAINER       => self.maintainer,
             MAINTAINER_EMAIL => self.maintainer_email,
             LICENSE          => self.license,
             PLATFORMS        => self.platforms,
             DEPENDENCIES     => self.dependencies,
             RECOMMENDATIONS  => self.recommendations,
             SUGGESTIONS      => self.suggestions,
             CONFLICTING      => self.conflicting,
             PROVIDING        => self.providing,
             REPLACING        => self.replacing,
             ATTRIBUTES       => self.attributes,
             GROUPINGS        => self.groupings,
             RECIPES          => self.recipes,
             VERSION          => self.version
           }
         end
   
         def to_json(*a)
           self.to_hash.to_json(*a)
         end
   
         def self.from_hash(o)
           cm = self.new()
           cm.from_hash(o)
           cm
         end
   
         def from_hash(o)
           @name                         = o[NAME] if o.has_key?(NAME)
           @description                  = o[DESCRIPTION] if o.has_key?(DESCRIPTION)
           @long_description             = o[LONG_DESCRIPTION] if o.has_key?(LONG_DESCRIPTION)
           @maintainer                   = o[MAINTAINER] if o.has_key?(MAINTAINER)
           @maintainer_email             = o[MAINTAINER_EMAIL] if o.has_key?(MAINTAINER_EMAIL)
           @license                      = o[LICENSE] if o.has_key?(LICENSE)
           @platforms                    = o[PLATFORMS] if o.has_key?(PLATFORMS)
           @dependencies                 = handle_deprecated_constraints(o[DEPENDENCIES]) if o.has_key?(DEPENDENCIES)
           @recommendations              = handle_deprecated_constraints(o[RECOMMENDATIONS]) if o.has_key?(RECOMMENDATIONS)
           @suggestions                  = handle_deprecated_constraints(o[SUGGESTIONS]) if o.has_key?(SUGGESTIONS)
           @conflicting                  = handle_deprecated_constraints(o[CONFLICTING]) if o.has_key?(CONFLICTING)
           @providing                    = o[PROVIDING] if o.has_key?(PROVIDING)
           @replacing                    = handle_deprecated_constraints(o[REPLACING]) if o.has_key?(REPLACING)
           @attributes                   = o[ATTRIBUTES] if o.has_key?(ATTRIBUTES)
           @groupings                    = o[GROUPINGS] if o.has_key?(GROUPINGS)
           @recipes                      = o[RECIPES] if o.has_key?(RECIPES)
           @version                      = o[VERSION] if o.has_key?(VERSION)
           self
         end
   
         def self.from_json(string)
           o = Chef::JSONCompat.from_json(string)
           self.from_hash(o)
         end
   
         def self.validate_json(json_str)
           o = Chef::JSONCompat.from_json(json_str)
           metadata = new()
           VERSION_CONSTRAINTS.each do |method_name, hash_key|
             if constraints = o[hash_key]
              constraints.each do |cb_name, constraints|
                metadata.send(method_name, cb_name, *Array(constraints))
              end
             end
           end
           true
         end
   
         def from_json(string)
           o = Chef::JSONCompat.from_json(string)
           from_hash(o)
         end
   
       private
   
         def new_args_format(caller_name, dep_name, version_constraints)
           if version_constraints.empty?
             ">= 0.0.0"
           elsif version_constraints.size == 1
             version_constraints.first
           else
             msg=<<-OBSOLETED
   The dependency specification syntax you are using is no longer valid. You may not
   specify more than one version constraint for a particular cookbook.
   Consult http://wiki.opscode.com/display/chef/Metadata for the updated syntax.
   
   Called by: #{caller_name} '#{dep_name}', #{version_constraints.map {|vc| vc.inspect}.join(", ")}
   Called from:
   #{caller[0...5].map {|line| "  " + line}.join("\n")}
   OBSOLETED
             raise Exceptions::ObsoleteDependencySyntax, msg
           end
         end
   
         def validate_version_constraint(caller_name, dep_name, constraint_str)
           Chef::VersionConstraint.new(constraint_str)
         rescue Chef::Exceptions::InvalidVersionConstraint => e
           Log.debug(e)
   
           msg=<<-INVALID
   The version constraint syntax you are using is not valid. If you recently
   upgraded to Chef 0.10.0, be aware that you no may longer use "<<" and ">>" for
   'less than' and 'greater than'; use '<' and '>' instead.
   Consult http://wiki.opscode.com/display/chef/Metadata for more information.
   
   Called by: #{caller_name} '#{dep_name}', '#{constraint_str}'
   Called from:
   #{caller[0...5].map {|line| "  " + line}.join("\n")}
   INVALID
           raise Exceptions::InvalidVersionConstraint, msg
         end
         # Verify that the given array is an array of strings
         #
         # Raise an exception if the members of the array are not Strings
         #
         # === Parameters
         # arry:: An array to be validated
         def validate_string_array(arry)
           if arry.kind_of?(Array)
             arry.each do |choice|
               validate( {:choice => choice}, {:choice => {:kind_of => String}} )
             end
           end
         end
   
         # For backwards compatibility, remap Boolean values to String
         #   true is mapped to "required"
         #   false is mapped to "optional"
         #
         # === Parameters
         # required_attr:: The value of options[:required]
         #
         # === Returns
         # required_attr:: "required", "recommended", or "optional"
         def remap_required_attribute(value)
           case value
           when true
             value = "required"
           when false
             value = "optional"
           end
           value
         end
   
         def validate_calculated_default_rule(options)
           calculated_conflict = ((options[:default].is_a?(Array) && !options[:default].empty?) ||
                                  (options[:default].is_a?(String) && !options[:default] != "")) &&
                                 options[:calculated] == true
           raise ArgumentError, "Default cannot be specified if calculated is true!" if calculated_conflict
         end
   
         def validate_choice_default_rule(options)
           return if !options[:choice].is_a?(Array) || options[:choice].empty?
   
           if options[:default].is_a?(String) && options[:default] != ""
             raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]) == nil
           end
   
           if options[:default].is_a?(Array) && !options[:default].empty?
             options[:default].each do |val|
               raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val) == nil
             end
           end
         end
   
         # This method translates version constraint strings from
         # cookbooks with the old format.
         #
         # Before we began respecting version constraints, we allowed
         # multiple constraints to be placed on cookbooks, as well as the
         # << and >> operators, which are now just < and >. For
         # specifications with more than one constraint, we return an
         # empty array (otherwise, we're silently abiding only part of
         # the contract they have specified to us). If there is only one
         # constraint, we are replacing the old << and >> with the new <
         # and >.
         def handle_deprecated_constraints(specification)
           specification.inject(Mash.new) do |acc, (cb, constraints)|
             constraints = Array(constraints)
             acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first.gsub(/>>/, '>').gsub(/<
             acc
           end
         end
   
       end
   
       #== Chef::Cookbook::MinimalMetadata
       # MinimalMetadata is a duck type of Cookbook::Metadata, used
       # internally by Chef Server when determining the optimal set of
       # cookbooks for a node.
       #
       # MinimalMetadata objects typically contain only enough information
       # to solve the cookbook collection for a run list, but not enough to
       # generate the proper response
       class MinimalMetadata < Metadata
         def initialize(name, params)
           @name = name
           from_hash(params)
         end
       end
   
   
     end
   end

lib/chef/knife/client_create.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientCreate < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         option :file,
           :short => "-f FILE",
           :long  => "--file FILE",
           :description => "Write the key to a file"
   
         option :admin,
           :short => "-a",
           :long  => "--admin",
           :description => "Create the client as an admin",
           :boolean => true
   
         banner "knife client create CLIENT (options)"
   
         def run
           @client_name = @name_args[0]
   
           if @client_name.nil?
             show_usage
             ui.fatal("You must specify a client name")
             exit 1
           end
   
           client = Chef::ApiClient.new
           client.name(@client_name)
           client.admin(config[:admin])
   
           output = edit_data(client)
   
           # Chef::ApiClient.save will try to create a client and if it exists will update it instead silently
           client = output.save
   
           # We only get a private_key on client creation, not on client update.
           if client['private_key']
             ui.info("Created #{output}")
     
             if config[:file]
               File.open(config[:file], "w") do |f|
                 f.print(client['private_key'])
               end
             else
               puts client['private_key']
             end
           else
             ui.error "Client '#{client['name']}' already exists"
           end
         end
       end
     end
   end
   

lib/chef/knife/data_bag_show.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class DataBagShow < Knife
   
         deps do
           require 'chef/data_bag'
           require 'chef/encrypted_data_bag_item'
         end
   
         banner "knife data bag show BAG [ITEM] (options)"
         category "data bag"
   
         option :secret,
         :short => "-s SECRET",
         :long  => "--secret ",
         :description => "The secret key to use to decrypt data bag item values"
   
         option :secret_file,
         :long => "--secret-file SECRET_FILE",
         :description => "A file containing the secret key to use to decrypt data bag item values"
   
         def read_secret
           if config[:secret]
             config[:secret]
           else
             Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
           end
         end
   
         def use_encryption
           if config[:secret] && config[:secret_file]
             stdout.puts "please specify only one of --secret, --secret-file"
             exit(1)
           end
           config[:secret] || config[:secret_file]
         end
   
         def run
           display = case @name_args.length
           when 2
             if use_encryption
               raw = Chef::EncryptedDataBagItem.load(@name_args[0],
                                                     @name_args[1],
                                                     read_secret)
               format_for_display(raw.to_hash)
             else
               format_for_display(Chef::DataBagItem.load(@name_args[0], @name_args[1]).raw_data)
             end
           when 1
             format_list_for_display(Chef::DataBag.load(@name_args[0]))
           else
             stdout.puts opt_parser
             exit(1)
           end
           output(display)
         end
       end
     end
   end
   

lib/chef/monkey_patches/dir.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   if RUBY_VERSION < "1.8.6" || RUBY_PLATFORM =~ /mswin|mingw32|windows/
     class Dir 
       class << self 
         alias_method :glob_, :glob 
         # Adds a Dir.glob to Ruby 1.8.5, for compat
         def glob(pattern, flags=0)
           raise ArgumentError unless (
             !pattern.nil? and (
               pattern.is_a? Array and !pattern.empty?
             ) or pattern.is_a? String
           )
           pattern.gsub!(/\\/, "/") if RUBY_PLATFORM =~ /mswin|mingw32|windows/
           [pattern].flatten.inject([]) { |r, p| r + glob_(p, flags) }
         end
         alias_method :[], :glob 
       end 
     end 
   end 

lib/chef/application/client.rb

   #
   # Author:: AJ Christensen (
   # Author:: Christopher Brown ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/application'
   require 'chef/client'
   require 'chef/config'
   require 'chef/daemon'
   require 'chef/log'
   require 'chef/rest'
   require 'chef/handler/error_report'
   
   
   class Chef::Application::Client < Chef::Application
   
     # Mimic self_pipe sleep from Unicorn to capture signals safely
     SELF_PIPE = []
   
     option :config_file,
       :short => "-c CONFIG",
       :long  => "--config CONFIG",
       :default => "/etc/chef/client.rb",
       :description => "The configuration file to use"
   
     option :log_level,
       :short        => "-l LEVEL",
       :long         => "--log_level LEVEL",
       :description  => "Set the log level (debug, info, warn, error, fatal)",
       :proc         => lambda { |l| l.to_sym }
   
     option :log_location,
       :short        => "-L LOGLOCATION",
       :long         => "--logfile LOGLOCATION",
       :description  => "Set the log file location, defaults to STDOUT - recommended for daemonizing",
       :proc         => nil
   
     option :help,
       :short        => "-h",
       :long         => "--help",
       :description  => "Show this message",
       :on           => :tail,
       :boolean      => true,
       :show_options => true,
       :exit         => 0
   
     option :user,
       :short => "-u USER",
       :long => "--user USER",
       :description => "User to set privilege to",
       :proc => nil
   
     option :group,
       :short => "-g GROUP",
       :long => "--group GROUP",
       :description => "Group to set privilege to",
       :proc => nil
   
     option :daemonize,
       :short => "-d",
       :long => "--daemonize",
       :description => "Daemonize the process",
       :proc => lambda { |p| true }
   
     option :pid_file,
       :short        => "-P PID_FILE",
       :long         => "--pid PIDFILE",
       :description  => "Set the PID file location, defaults to /tmp/chef-client.pid",
       :proc         => nil
   
     option :interval,
       :short => "-i SECONDS",
       :long => "--interval SECONDS",
       :description => "Run chef-client periodically, in seconds",
       :proc => lambda { |s| s.to_i }
   
     option :once,
       :long => "--once",
       :description => "Cancel any interval or splay options, run chef once and exit",
       :boolean => true
   
     option :json_attribs,
       :short => "-j JSON_ATTRIBS",
       :long => "--json-attributes JSON_ATTRIBS",
       :description => "Load attributes from a JSON file or URL",
       :proc => nil
   
     option :node_name,
       :short => "-N NODE_NAME",
       :long => "--node-name NODE_NAME",
       :description => "The node name for this client",
       :proc => nil
   
     option :splay,
       :short => "-s SECONDS",
       :long => "--splay SECONDS",
       :description => "The splay time for running at intervals, in seconds",
       :proc => lambda { |s| s.to_i }
   
     option :chef_server_url,
       :short => "-S CHEFSERVERURL",
       :long => "--server CHEFSERVERURL",
       :description => "The chef server URL",
       :proc => nil
   
     option :validation_key,
       :short        => "-K KEY_FILE",
       :long         => "--validation_key KEY_FILE",
       :description  => "Set the validation key file location, used for registering new clients",
       :proc         => nil
   
     option :client_key,
       :short        => "-k KEY_FILE",
       :long         => "--client_key KEY_FILE",
       :description  => "Set the client key file location",
       :proc         => nil
   
     option :environment,
       :short        => '-E ENVIRONMENT',
       :long         => '--environment ENVIRONMENT',
       :description  => 'Set the Chef Environment on the node'
   
     option :version,
       :short        => "-v",
       :long         => "--version",
       :description  => "Show chef version",
       :boolean      => true,
       :proc         => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
       :exit         => 0
   
     attr_reader :chef_client_json
   
     def initialize
       super
   
       @chef_client = nil
       @chef_client_json = nil
     end
   
     # Reconfigure the chef client
     # Re-open the JSON attributes and load them into the node
     def reconfigure
       super
   
       Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url
       unless Chef::Config[:exception_handlers].any? {|h| Chef::Handler::ErrorReport === h}
         Chef::Config[:exception_handlers] << Chef::Handler::ErrorReport.new
       end
   
       if Chef::Config[:daemonize]
         Chef::Config[:interval] ||= 1800
       end
   
       if Chef::Config[:once]
         Chef::Config[:interval] = nil
         Chef::Config[:splay] = nil
       end
   
       if Chef::Config[:json_attribs]
         begin
           json_io = case Chef::Config[:json_attribs]
                     when /^(http|https):\/\//
                       @rest = Chef::REST.new(Chef::Config[:json_attribs], nil, nil)
                       @rest.get_rest(Chef::Config[:json_attribs], true).open
                     else
                       open(Chef::Config[:json_attribs])
                     end
         rescue SocketError => error
           Chef::Application.fatal!("I cannot connect to #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::ENOENT => error
           Chef::Application.fatal!("I cannot find #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::EACCES => error
           Chef::Application.fatal!("Permissions are incorrect on #{Chef::Config[:json_attribs]}. Please chmod a+r #{Chef::Config[:json_attribs]}", 2)
         rescue Exception => error
           Chef::Application.fatal!("Got an unexpected error reading #{Chef::Config[:json_attribs]}: #{error.message}", 2)
         end
   
         begin
           @chef_client_json = Chef::JSONCompat.from_json(json_io.read)
           json_io.close unless json_io.closed?
         rescue JSON::ParserError => error
           Chef::Application.fatal!("Could not parse the provided JSON file (#{Chef::Config[:json_attribs]})!: " + error.message, 2)
         end
       end
     end
   
     def configure_logging
       super
       Mixlib::Authentication::Log.use_log_devices( Chef::Log )
       Ohai::Log.use_log_devices( Chef::Log )
     end
   
     def setup_application
       Chef::Daemon.change_privilege
     end
   
     # Run the chef client, optionally daemonizing or looping at intervals.
     def run_application
       unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
         SELF_PIPE.replace IO.pipe
   
         trap("USR1") do
           Chef::Log.info("SIGUSR1 received, waking up")
           SELF_PIPE[1].putc('.') # wakeup master process from select
         end
       end
   
       if Chef::Config[:version]
         puts "Chef version: #{::Chef::VERSION}"
       end
   
       if Chef::Config[:daemonize]
         Chef::Daemon.daemonize("chef-client")
       end
   
       loop do
         begin
           if Chef::Config[:splay]
             splay = rand Chef::Config[:splay]
             Chef::Log.debug("Splay sleep #{splay} seconds")
             sleep splay
           end
           @chef_client = Chef::Client.new(@chef_client_json)
           @chef_client_json = nil
   
           @chef_client.run
           @chef_client = nil
           if Chef::Config[:interval]
             Chef::Log.debug("Sleeping for #{Chef::Config[:interval]} seconds")
             unless SELF_PIPE.empty?
               client_sleep Chef::Config[:interval]
             else
               # Windows
               sleep Chef::Config[:interval]
             end
           else
             Chef::Application.exit! "Exiting", 0
           end
         rescue Chef::Application::Wakeup => e
           Chef::Log.debug("Received Wakeup signal.  Starting run.")
           next
         rescue SystemExit => e
           raise
         rescue Exception => e
           if Chef::Config[:interval]
             Chef::Log.error("#{e.class}: #{e}")
             Chef::Application.debug_stacktrace(e)
             Chef::Log.error("Sleeping for #{Chef::Config[:interval]} seconds before trying again")
             unless SELF_PIPE.empty?
               client_sleep Chef::Config[:interval]
             else
               # Windows
               sleep Chef::Config[:interval]
             end
             retry
           else
             Chef::Application.debug_stacktrace(e)
             Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
           end
         ensure
           GC.start
         end
       end
     end
   
     private 
   
     def client_sleep(sec)
       IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
       SELF_PIPE[0].getc
     end
   end

lib/chef/provider/env/windows.rb

   #
   # Author:: Doug MacEachern ()
   # Copyright:: Copyright (c) 2010 VMware, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   if RUBY_PLATFORM =~ /mswin|mingw32|windows/
     require 'ruby-wmi'
     require 'Win32API'
   end
   
   class Chef
     class Provider
       class Env
         class Windows < Chef::Provider::Env
   
           def create_env
             obj = env_obj(@new_resource.key_name)
             unless obj
               obj = WIN32OLE.connect("winmgmts://").get("Win32_Environment").spawninstance_
               obj.name = @new_resource.key_name
               obj.username = ""
             end
             obj.variablevalue = @new_resource.value
             obj.put_
             broadcast_env_change
           end
   
           def delete_env
             obj = env_obj(@new_resource.key_name)
             if obj
               obj.delete_
               broadcast_env_change
             end
           end
   
           def env_value(key_name)
             obj = env_obj(key_name)
             return obj ? obj.variablevalue : nil
           end
   
           def env_obj(key_name)
             WMI::Win32_Environment.find(:first,
                                         :conditions => { :name => key_name })
           end
   
           #see: http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx
           HWND_BROADCAST = 0xffff
           WM_SETTINGCHANGE = 0x001A
           SMTO_BLOCK = 0x0001
           SMTO_ABORTIFHUNG = 0x0002
           SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
   
           def broadcast_env_change
             result = 0
             flags = SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG
             @send_message ||= Win32API.new('user32', 'SendMessageTimeout', 'LLLPLLP', 'L')
             @send_message.call(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 'Environment', flags, 5000, result)
           end
         end
       end
     end
   end

lib/chef/knife/bootstrap.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   require 'erubis'
   
   class Chef
     class Knife
       class Bootstrap < Knife
   
         deps do
           require 'chef/knife/core/bootstrap_context'
           require 'chef/json_compat'
           require 'tempfile'
           require 'highline'
           require 'net/ssh'
           require 'net/ssh/multi'
           Chef::Knife::Ssh.load_deps
         end
   
         banner "knife bootstrap FQDN (options)"
   
         option :ssh_user,
           :short => "-x USERNAME",
           :long => "--ssh-user USERNAME",
           :description => "The ssh username",
           :default => "root"
   
         option :ssh_password,
           :short => "-P PASSWORD",
           :long => "--ssh-password PASSWORD",
           :description => "The ssh password"
   
         option :ssh_port,
           :short => "-p PORT",
           :long => "--ssh-port PORT",
           :description => "The ssh port",
           :default => "22",
           :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
   
         option :identity_file,
           :short => "-i IDENTITY_FILE",
           :long => "--identity-file IDENTITY_FILE",
           :description => "The SSH identity file used for authentication"
   
         option :chef_node_name,
           :short => "-N NAME",
           :long => "--node-name NAME",
           :description => "The Chef node name for your new node"
   
         option :prerelease,
           :long => "--prerelease",
           :description => "Install the pre-release chef gems"
   
         option :bootstrap_version,
           :long => "--bootstrap-version VERSION",
           :description => "The version of Chef to install",
           :proc => lambda { |v| Chef::Config[:knife][:bootstrap_version] = v }
   
         option :bootstrap_proxy,
           :long => "--bootstrap-proxy PROXY_URL",
           :description => "The proxy server for the node being bootstrapped",
           :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
   
         option :distro,
           :short => "-d DISTRO",
           :long => "--distro DISTRO",
           :description => "Bootstrap a distro using a template",
           :default => "ubuntu10.04-gems"
   
         option :use_sudo,
           :long => "--sudo",
           :description => "Execute the bootstrap via sudo",
           :boolean => true
   
         option :template_file,
           :long => "--template-file TEMPLATE",
           :description => "Full path to location of template to use",
           :default => false
   
         option :run_list,
           :short => "-r RUN_LIST",
           :long => "--run-list RUN_LIST",
           :description => "Comma separated list of roles/recipes to apply",
           :proc => lambda { |o| o.split(/[\s,]+/) },
           :default => []
   
         option :no_host_key_verify,
           :long => "--no-host-key-verify",
           :description => "Disable host key verification",
           :boolean => true,
           :default => false
   
         def load_template(template=nil)
           # Are we bootstrapping using an already shipped template?
           if config[:template_file]
             bootstrap_files = config[:template_file]
           else
             bootstrap_files = []
             bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap', "#{config[:distro]}.erb")
             bootstrap_files << File.join(@@chef_config_dir, "bootstrap", "#{config[:distro]}.erb")
             bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{config[:distro]}.erb")
             bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
             bootstrap_files.flatten!
           end
   
           template = Array(bootstrap_files).find do |bootstrap_template|
             Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
             File.exists?(bootstrap_template)
           end
   
           unless template
             ui.info("Can not find bootstrap definition for #{config[:distro]}")
             raise Errno::ENOENT
           end
   
           Chef::Log.debug("Found bootstrap template in #{File.dirname(template)}")
   
           IO.read(template).chomp
         end
   
         def render_template(template=nil)
           context = Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config)
           Erubis::Eruby.new(template).evaluate(context)
         end
   
         def run
   
           validate_name_args!
           @node_name = Array(@name_args).first
           # back compat--templates may use this setting:
           config[:server_name] = @node_name
   
           $stdout.sync = true
   
           ui.info("Bootstrapping Chef on #{ui.color(@node_name, :bold)}")
   
           begin
             knife_ssh.run
           rescue Net::SSH::AuthenticationFailed
             unless config[:ssh_password]
               puts "Failed to authenticate #{config[:ssh_user]} - trying password auth"
               knife_ssh_with_password_auth.run
             end
           end
         end
   
         def validate_name_args!
           if Array(@name_args).first.nil?
             ui.error("Must pass an FQDN or ip to bootstrap")
             exit 1
           end
         end
   
         def server_name
           Array(@name_args).first
         end
   
         def knife_ssh
           ssh = Chef::Knife::Ssh.new
           ssh.ui = ui
           ssh.name_args = [ server_name, ssh_command ]
           ssh.config[:ssh_user] = config[:ssh_user]
           ssh.config[:ssh_password] = config[:ssh_password]
           ssh.config[:ssh_port] = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
           ssh.config[:identity_file] = config[:identity_file]
           ssh.config[:manual] = true
           ssh.config[:no_host_key_verify] = config[:no_host_key_verify]
           ssh.config[:on_error] = :raise
           ssh
         end
   
         def knife_ssh_with_password_auth
           ssh = knife_ssh
           ssh.config[:identity_file] = nil
           ssh.config[:ssh_password] = ssh.get_password
           ssh
         end
   
         def ssh_command
           command = render_template(load_template(config[:bootstrap_template]))
   
           if config[:use_sudo]
             command = "sudo #{command}"
           end
   
           command
         end
   
       end
     end
   end

lib/chef/application/solo.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef'
   require 'chef/application'
   require 'chef/client'
   require 'chef/config'
   require 'chef/daemon'
   require 'chef/log'
   require 'chef/rest'
   require 'open-uri'
   require 'fileutils'
   
   class Chef::Application::Solo < Chef::Application
   
     option :config_file,
       :short => "-c CONFIG",
       :long  => "--config CONFIG",
       :default => "/etc/chef/solo.rb",
       :description => "The configuration file to use"
   
     option :log_level,
       :short        => "-l LEVEL",
       :long         => "--log_level LEVEL",
       :description  => "Set the log level (debug, info, warn, error, fatal)",
       :proc         => lambda { |l| l.to_sym }
   
     option :log_location,
       :short        => "-L LOGLOCATION",
       :long         => "--logfile LOGLOCATION",
       :description  => "Set the log file location, defaults to STDOUT",
       :proc         => nil
   
     option :help,
       :short        => "-h",
       :long         => "--help",
       :description  => "Show this message",
       :on           => :tail,
       :boolean      => true,
       :show_options => true,
       :exit         => 0
   
     option :user,
       :short => "-u USER",
       :long => "--user USER",
       :description => "User to set privilege to",
       :proc => nil
   
     option :group,
       :short => "-g GROUP",
       :long => "--group GROUP",
       :description => "Group to set privilege to",
       :proc => nil
   
     option :daemonize,
       :short => "-d",
       :long => "--daemonize",
       :description => "Daemonize the process",
       :proc => lambda { |p| true }
   
     option :interval,
       :short => "-i SECONDS",
       :long => "--interval SECONDS",
       :description => "Run chef-client periodically, in seconds",
       :proc => lambda { |s| s.to_i }
   
     option :json_attribs,
       :short => "-j JSON_ATTRIBS",
       :long => "--json-attributes JSON_ATTRIBS",
       :description => "Load attributes from a JSON file or URL",
       :proc => nil
   
     option :node_name,
       :short => "-N NODE_NAME",
       :long => "--node-name NODE_NAME",
       :description => "The node name for this client",
       :proc => nil
   
     option :splay,
       :short => "-s SECONDS",
       :long => "--splay SECONDS",
       :description => "The splay time for running at intervals, in seconds",
       :proc => lambda { |s| s.to_i }
   
     option :recipe_url,
         :short => "-r RECIPE_URL",
         :long => "--recipe-url RECIPE_URL",
         :description => "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache.",
         :proc => nil
   
     option :version,
       :short        => "-v",
       :long         => "--version",
       :description  => "Show chef version",
       :boolean      => true,
       :proc         => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
       :exit         => 0
   
     attr_reader :chef_solo_json
   
     def initialize
       super
       @chef_solo = nil
       @chef_solo_json = nil
     end
   
     def reconfigure
       super
   
       Chef::Config[:solo] = true
   
       if Chef::Config[:daemonize]
         Chef::Config[:interval] ||= 1800
       end
   
       if Chef::Config[:json_attribs]
         begin
           json_io = case Chef::Config[:json_attribs]
                     when /^(http|https):\/\//
                       @rest = Chef::REST.new(Chef::Config[:json_attribs], nil, nil)
                       @rest.get_rest(Chef::Config[:json_attribs], true).open
                     else
                       open(Chef::Config[:json_attribs])
                     end
         rescue SocketError => error
           Chef::Application.fatal!("I cannot connect to #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::ENOENT => error
           Chef::Application.fatal!("I cannot find #{Chef::Config[:json_attribs]}", 2)
         rescue Errno::EACCES => error
           Chef::Application.fatal!("Permissions are incorrect on #{Chef::Config[:json_attribs]}. Please chmod a+r #{Chef::Config[:json_attribs]}", 2)
         rescue Exception => error
           Chef::Application.fatal!("Got an unexpected error reading #{Chef::Config[:json_attribs]}: #{error.message}", 2)
         end
   
         begin
           @chef_solo_json = Chef::JSONCompat.from_json(json_io.read)
           json_io.close unless json_io.closed?
         rescue JSON::ParserError => error
           Chef::Application.fatal!("Could not parse the provided JSON file (#{Chef::Config[:json_attribs]})!: " + error.message, 2)
         end
       end
   
       if Chef::Config[:recipe_url]
         cookbooks_path = Array(Chef::Config[:cookbook_path]).detect{|e| e =~ /\/cookbooks\/*$/ }
         recipes_path = File.expand_path(File.join(cookbooks_path, '..'))
         target_file = File.join(recipes_path, 'recipes.tgz')
   
         Chef::Log.debug "Creating path #{recipes_path} to extract recipes into"
         FileUtils.mkdir_p recipes_path
         path = File.join(recipes_path, 'recipes.tgz')
         File.open(path, 'wb') do |f|
           open(Chef::Config[:recipe_url]) do |r|
             f.write(r.read)
           end
         end
         Chef::Mixin::Command.run_command(:command => "tar zxvfC #{path} #{recipes_path}")
       end
     end
   
     def setup_application
       Chef::Daemon.change_privilege
     end
   
     def run_application
       if Chef::Config[:daemonize]
         Chef::Daemon.daemonize("chef-client")
       end
   
       loop do
         begin
           if Chef::Config[:splay]
             splay = rand Chef::Config[:splay]
             Chef::Log.debug("Splay sleep #{splay} seconds")
             sleep splay
           end
   
           @chef_solo = Chef::Client.new(@chef_solo_json)
           @chef_solo.run
           @chef_solo = nil
           if Chef::Config[:interval]
             Chef::Log.debug("Sleeping for #{Chef::Config[:interval]} seconds")
             sleep Chef::Config[:interval]
           else
             Chef::Application.exit! "Exiting", 0
           end
         rescue SystemExit => e
           raise
         rescue Exception => e
           if Chef::Config[:interval]
             Chef::Log.error("#{e.class}: #{e}")
             Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
             Chef::Log.fatal("Sleeping for #{Chef::Config[:interval]} seconds before trying again")
             sleep Chef::Config[:interval]
             retry
           else
             Chef::Application.debug_stacktrace(e)
             Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
           end
         ensure
           GC.start
         end
       end
     end
   end

lib/chef/knife/cookbook_site_download.rb

   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteDownload < Knife
   
         attr_reader :version
   
         banner "knife cookbook site download COOKBOOK [VERSION] (options)"
         category "cookbook site"
   
         option :file,
           :short => "-f FILE",
           :long => "--file FILE",
           :description => "The filename to write to"
   
         option :force,
           :long => "--force",
           :description => "Force download deprecated version"
   
         def run
           current = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{name_args[0]}")
           if current["deprecated"] == true
             replacement = File.basename(current["replacement"])
             ui.warn("DEPRECATION: This cookbook has been deprecated. It has been replaced by #{replacement}.")
             unless config[:force]
               ui.warn("Use --force to force download deprecated cookbook.")
               return
             end
           end
           cookbook_data = if @name_args.length == 1
                             noauth_rest.get_rest(current["latest_version"])
                           else
                             noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{name_args[0]}/versions/#{name_args[1].gsub('.', '_')}")
                           end
   
           @version = cookbook_data['version']
           unless config[:file]
             config[:file] = File.join(Dir.pwd, "#{@name_args[0]}-#{cookbook_data['version']}.tar.gz")
           end
           ui.info("Downloading #{@name_args[0]} from the cookbooks site at version #{cookbook_data['version']} to #{config[:file]}")
           noauth_rest.sign_on_redirect = false
           tf = noauth_rest.get_rest(cookbook_data["file"], true)
   
           FileUtils.cp(tf.path, config[:file])
           ui.info("Cookbook saved: #{config[:file]}")
         end
   
       end
     end
   end
   
   

lib/chef/index_queue/consumer.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module IndexQueue
       module Consumer
         module ClassMethods
           def expose(*methods)
             @exposed_methods = Array(@exposed_methods)
             @exposed_methods += methods
           end
           
           def exposed_methods
             @exposed_methods || []
           end
           
           def whitelisted?(method_name)
             exposed_methods.include?(method_name)
           end
         end
         
         def self.included(including_class)
           including_class.send(:extend, ClassMethods)
         end
         
         def run
           Chef::Log.debug("Starting Index Queue Consumer")
           AmqpClient.instance.queue # triggers connection setup
           
           begin
             AmqpClient.instance.queue.subscribe(:ack => true, :timeout => false) do |message|
               call_action_for_message(message)
             end
           rescue Bunny::ConnectionError, Errno::ECONNRESET, Bunny::ServerDownError
             AmqpClient.instance.disconnected!
             Chef::Log.warn "Connection to rabbitmq lost. attempting to reconnect"
             sleep 1
             retry
           end
         end
         alias :start :run
         
         def call_action_for_message(message)
           amqp_payload  = Chef::JSONCompat.from_json(message[:payload], :create_additions => false, :max_nesting => false)
           action        = amqp_payload["action"].to_sym
           app_payload   = amqp_payload["payload"]
           assert_method_whitelisted(action)
           send(action, app_payload)
         end
         
         private
         
         def assert_method_whitelisted(method_name)
           unless self.class.whitelisted?(method_name)
             raise ArgumentError, "non-exposed method #{method_name} called via index queue"
           end
         end
         
       end
     end
   end

lib/chef/run_list/versioned_recipe_list.rb

   #
   # Author:: Stephen Delano ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   require 'chef/version_class'
   require 'chef/version_constraint'
   
   # Why does this class exist?
   # Why did we not just modify RunList/RunListItem?
   class Chef
     class RunList
       class VersionedRecipeList < Array
   
         def initialize
           super
           @versions = Hash.new
         end
   
         def add_recipe(name, version=nil)
           if version && @versions.has_key?(name)
             unless Chef::Version.new(@versions[name]) == Chef::Version.new(version)
               raise Chef::Exceptions::CookbookVersionConflict, "Run list requires #{name} at versions #{@versions[name]} and #{version}"
             end
           end
           @versions[name] = version if version
           self << name unless self.include?(name)
         end
   
         def with_versions
           self.map {|recipe_name| {:name => recipe_name, :version => @versions[recipe_name]}}
         end
   
         # Return an Array of Hashes, each of the form:
         #  {:name => RECIPE_NAME, :version_constraint => Chef::VersionConstraint }
         def with_version_constraints
           self.map do |recipe_name|
             constraint = Chef::VersionConstraint.new(@versions[recipe_name])
             { :name => recipe_name, :version_constraint => constraint }
           end
         end
   
         # Return an Array of Strings, each of the form:
         #  "NAME@VERSION"
         def with_version_constraints_strings
           self.map do |recipe_name|
             if @versions[recipe_name]
               "#{recipe_name}@#{@versions[recipe_name]}"
             else
               recipe_name
             end
           end
         end
       end
     end
   end

lib/chef/provider/group/gpasswd.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/group/groupadd'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Provider
       class Group
         class Gpasswd < Chef::Provider::Group::Groupadd
   
           include Chef::Mixin::ShellOut
   
           def load_current_resource
             super
   
             raise Chef::Exceptions::Group, "Could not find binary /usr/bin/gpasswd for #{@new_resource}" unless ::File.exists?("/usr/bin/gpasswd")
           end
   
           def modify_group_members
             unless @new_resource.members.empty?
               if(@new_resource.append)
                 @new_resource.members.each do |member|
                   Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}")
                   shell_out!("gpasswd -a #{member} #{@new_resource.group_name}")
                 end
               else
                 Chef::Log.debug("#{@new_resource} setting group members to #{@new_resource.members.join(', ')}")
                 shell_out!("gpasswd -M #{@new_resource.members.join(',')} #{@new_resource.group_name}")
               end
             else
               Chef::Log.debug("#{@new_resource} not changing group members, the group has no members")
             end
           end
         end
       end
     end
   end

lib/chef/resource_definition.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mixin/from_file'
   require 'chef/mixin/params_validate'
   
   class Chef
     class ResourceDefinition
       
       include Chef::Mixin::FromFile
       include Chef::Mixin::ParamsValidate
       
       attr_accessor :name, :params, :recipe, :node
       
       def initialize(node=nil)
         @name = nil
         @params = Hash.new
         @recipe = nil
         @node = node
       end
       
       def define(resource_name, prototype_params=nil, &block)
         unless resource_name.kind_of?(Symbol)
           raise ArgumentError, "You must use a symbol when defining a new resource!"
         end
         @name = resource_name
         if prototype_params
           unless prototype_params.kind_of?(Hash)
             raise ArgumentError, "You must pass a hash as the prototype parameters for a definition."
           end
           @params = prototype_params
         end
         if Kernel.block_given?
           @recipe = block
         else
           raise ArgumentError, "You must pass a block to a definition."
         end
         true
       end
       
       # When we do the resource definition, we're really just setting new values for
       # the paramaters we prototyped at the top.  This method missing is as simple as
       # it gets.
       def method_missing(symbol, *args)
         @params[symbol] = args.length == 1 ? args[0] : args
       end
       
       def to_s
         "#{name.to_s}"
       end
     end
   end

lib/chef/provider/group/suse.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 OpsCode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/group/groupadd'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Provider
       class Group
         class Suse < Chef::Provider::Group::Groupadd
   
           include Chef::Mixin::ShellOut
   
           def load_current_resource
             super
   
             raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource}" unless ::File.exists?("/usr/sbin/groupmod")
           end
   
           def modify_group_members
             unless @new_resource.members.empty?
               if(@new_resource.append)
                 @new_resource.members.each do |member|
                   Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}")
                   shell_out!("groupmod -A #{member} #{@new_resource.group_name}")
                 end
               else
                 Chef::Log.debug("#{@new_resource} setting group members to #{@new_resource.members.join(', ')}")
                 shell_out!("groupmod -A #{@new_resource.members.join(',')} #{@new_resource.group_name}")
               end
             else
               Chef::Log.debug("#{@new_resource} not changing group members, the group has no members")
             end
           end
         end
       end
     end
   end

lib/chef/cookbook/chefignore.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Cookbook
       class Chefignore
   
         COMMENTS_AND_WHITESPACE = /^\w*(?:#.*)?$/
   
         attr_reader :ignores
   
         def initialize(ignore_file_or_repo)
           @ignore_file = find_ignore_file(ignore_file_or_repo)
           @ignores = parse_ignore_file
         end
   
         def remove_ignores_from(file_list)
           Array(file_list).inject([]) do |unignored, file|
             ignored?(file) ? unignored : unignored << file
           end
         end
   
         def ignored?(file_name)
           @ignores.any? {|glob| File.fnmatch?(glob, file_name)}
         end
   
         private
   
         def parse_ignore_file
           ignore_globs = []
           if File.exist?(@ignore_file) && File.readable?(@ignore_file)
             File.foreach(@ignore_file) do |line|
               ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE
             end
           else
             Chef::Log.debug("No chefignore file found at #@ignore_file no files will be ignored")
           end
           ignore_globs
         end
   
         def find_ignore_file(path)
           if File.basename(path) =~ /chefignore/
             path
           else
             File.join(path, 'chefignore')
           end
         end
       end
     end
   end
   

lib/chef/provider/script.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'tempfile'
   require 'chef/provider/execute'
   
   class Chef
     class Provider
       class Script < Chef::Provider::Execute
   
         def action_run
           script_file.puts(@new_resource.code)
           script_file.close
   
           set_owner_and_group
   
           @new_resource.command("\"#{@new_resource.interpreter}\" #{@new_resource.flags} \"#{script_file.path}\"")
           super
         ensure
           unlink_script_file
         end
   
         def set_owner_and_group
           # FileUtils itself implements a no-op if +user+ or +group+ are nil
           # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file')
           # as an unprivileged user.
           FileUtils.chown(@new_resource.user, @new_resource.group, script_file.path)
         end
   
         def script_file
           @script_file ||= Tempfile.open("chef-script")
         end
   
         def unlink_script_file
           @script_file && @script_file.close!
         end
   
       end
     end
   end

lib/chef/run_list.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Nuo Yan ()
   # Author:: Tim Hinderliter ()
   # Author:: Christopher Walters ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2008-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/run_list/run_list_item'
   require 'chef/run_list/run_list_expansion'
   require 'chef/run_list/versioned_recipe_list'
   require 'chef/mixin/params_validate'
   
   class Chef
     class RunList
       include Enumerable
       include Chef::Mixin::ParamsValidate
   
       # @run_list_items is an array of RunListItems that describe the items to 
       # execute in order. RunListItems can load from and convert to the string
       # forms users set on roles and nodes.
       # For example:
       #   @run_list_items = ['recipe[foo::bar]', 'role[webserver]']
       # Thus,
       #   self.role_names would return ['webserver']
       #   self.recipe_names would return ['foo::bar']
       attr_reader :run_list_items
   
       # For backwards compat
       alias :run_list :run_list_items
   
       def initialize(*run_list_items)
         @run_list_items = run_list_items.map { |i| coerce_to_run_list_item(i) }
       end
   
       def role_names
         @run_list_items.inject([]){|memo, run_list_item| memo << run_list_item.name if run_list_item.role? ; memo}
       end
   
       alias :roles :role_names
   
       def recipe_names
         @run_list_items.inject([]){|memo, run_list_item| memo << run_list_item.name if run_list_item.recipe? ; memo}
       end
   
       alias :recipes :recipe_names
   
       # Add an item of the form "recipe[foo::bar]" or "role[webserver]";
       # takes a String or a RunListItem
       def <<(run_list_item)
         run_list_item = coerce_to_run_list_item(run_list_item)
         @run_list_items << run_list_item unless @run_list_items.include?(run_list_item)
         self
       end
   
       alias :push :<<
   
       def ==(other)
         if other.kind_of?(Chef::RunList)
           other.run_list_items == @run_list_items
         else
           return false unless other.respond_to?(:size) && (other.size == @run_list_items.size)
           other_run_list_items = other.dup
   
           other_run_list_items.map! { |item| coerce_to_run_list_item(item) }
           other_run_list_items == @run_list_items
         end
       end
   
       def to_s
         @run_list_items.join(", ")
       end
   
       def to_json(*args)
         to_a.to_json(*args)
       end
   
       def empty?
         @run_list_items.length == 0 ? true : false
       end
   
       def [](pos)
         @run_list_items[pos]
       end
   
       def []=(pos, item)
         @run_list_items[pos] = parse_entry(item)
       end
   
       def each(&block)
         @run_list_items.each { |i| block.call(i) }
       end
   
       def each_index(&block)
         @run_list_items.each_index { |i| block.call(i) }
       end
   
       def include?(item)
         @run_list_items.include?(parse_entry(item))
       end
   
       def reset!(*args)
         @run_list_items.clear
         args.flatten.each do |item|
           if item.kind_of?(Chef::RunList)
             item.each { |r| self << r }
           else
             self << item
           end
         end
         self
       end
   
       def remove(item)
         @run_list_items.delete_if{|i| i == item}
         self
       end
       alias :delete :remove
   
       # Expands this run_list: recursively expand roles into their included
       # recipes.
       # Returns a RunListExpansion object.
       def expand(environment, data_source='server', expansion_opts={})
         expansion = expansion_for_data_source(environment, data_source, expansion_opts)
         expansion.expand
         expansion
       end
   
       # Converts a string run list entry to a RunListItem object.
       def parse_entry(entry)
         RunListItem.new(entry)
       end
   
       def coerce_to_run_list_item(item)
         item.kind_of?(RunListItem) ? item : parse_entry(item)
       end
   
       def expansion_for_data_source(environment, data_source, opts={})
         case data_source.to_s
         when 'disk'
           RunListExpansionFromDisk.new(environment, @run_list_items)
         when 'server'
           RunListExpansionFromAPI.new(environment, @run_list_items, opts[:rest])
         when 'couchdb'
           RunListExpansionFromCouchDB.new(environment, @run_list_items, opts[:couchdb])
         end
       end
   
   
     end
   end
   

lib/chef/knife/client_reregister.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientReregister < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         banner "knife client reregister CLIENT (options)"
   
         option :file,
           :short => "-f FILE",
           :long  => "--file FILE",
           :description => "Write the key to a file"
   
         def run
           @client_name = @name_args[0]
   
           if @client_name.nil?
             show_usage
             ui.fatal("You must specify a client name")
             exit 1
           end
   
           client = Chef::ApiClient.load(@client_name)
           key = client.save(new_key=true)
           if config[:file]
             File.open(config[:file], "w") do |f|
               f.print(key['private_key'])
             end
           else
             ui.msg key['private_key']
           end
         end
       end
     end
   end

lib/chef/checksum.rb

   #
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/log'
   require 'uuidtools'
   
   class Chef
     # == Chef::Checksum
     # Checksum for an individual file; e.g., used for sandbox/cookbook uploading
     # to track which files the system already manages.
     class Checksum
       attr_accessor :checksum, :create_time
       attr_accessor :couchdb_id, :couchdb_rev
   
       # When a Checksum commits a sandboxed file to its final home in the checksum
       # repo, this attribute will have the original on-disk path where the file
       # was stored; it will be used if the commit is reverted to restore the sandbox
       # to the pre-commit state.
       attr_reader :original_committed_file_location
   
       DESIGN_DOCUMENT = {
         "version" => 1,
         "language" => "javascript",
         "views" => {
           "all" => {
             "map" => <<-EOJS
             function(doc) { 
               if (doc.chef_type == "checksum") {
                 emit(doc.checksum, doc);
               }
             }
             EOJS
           },
         }
       }
       
       # Creates a new Chef::Checksum object.
       # === Arguments
       # checksum::: the MD5 content hash of the file
       # couchdb::: An instance of Chef::CouchDB
       #
       # === Returns
       # object:: Duh. :)
       def initialize(checksum=nil, couchdb=nil)
         @create_time = Time.now.iso8601
         @checksum = checksum
         @original_committed_file_location = nil
       end
       
       def to_json(*a)
         result = {
           :checksum => checksum,
           :create_time => create_time,
           :json_class => self.class.name,
           :chef_type => 'checksum',
   
           # For Chef::CouchDB (id_to_name, name_to_id)
           :name => checksum
         }
         result.to_json(*a)
       end
   
       def self.json_create(o)
         checksum = new(o['checksum'])
         checksum.create_time = o['create_time']
   
         if o.has_key?('_rev')
           checksum.couchdb_rev = o["_rev"]
           o.delete("_rev")
         end
         if o.has_key?("_id")
           checksum.couchdb_id = o["_id"]
           o.delete("_id")
         end
         checksum
       end
   
   
       ##
       # On-Disk Checksum File Repo (Chef Server API)
       ##
   
       def file_location
         File.join(checksum_repo_directory, checksum)
       end
   
       def checksum_repo_directory
         File.join(Chef::Config.checksum_path, checksum[0..1])
       end
   
       # Moves the given +sandbox_file+ into the checksum repo using the path
       # given by +file_location+ and saves the Checksum to the database
       def commit_sandbox_file(sandbox_file)
         @original_committed_file_location = sandbox_file
         Chef::Log.info("Commiting sandbox file: move #{sandbox_file} to #{file_location}")
         FileUtils.mkdir_p(checksum_repo_directory)
         File.rename(sandbox_file, file_location)
         cdb_save
       end
   
       # Moves the checksum file back to its pre-commit location and deletes
       # the checksum object from the database, effectively undoing +commit_sandbox_file+.
       # Raises Chef::Exceptions::IllegalChecksumRevert if the original file location
       # is unknown, which is will be the case if commit_sandbox_file was not
       # previously called
       def revert_sandbox_file_commit
         unless original_committed_file_location
           raise Chef::Exceptions::IllegalChecksumRevert, "Checksum #{self.inspect} cannot be reverted because the original sandbox file location is not known"
         end
   
         Chef::Log.warn("Reverting sandbox file commit: moving #{file_location} back to #{original_committed_file_location}")
         File.rename(file_location, original_committed_file_location)
         cdb_destroy
       end
   
       # Removes the on-disk file backing this checksum object, then removes it
       # from the database
       def purge
         purge_file
         cdb_destroy
       end
   
       ##
       # Couchdb
       ##
   
       def self.create_design_document(couchdb=nil)
         (couchdb || Chef::CouchDB.new).create_design_document("checksums", DESIGN_DOCUMENT)
       end
       
       def self.cdb_list(inflate=false, couchdb=nil)
         rs = (couchdb || Chef::CouchDB.new).list("checksums", inflate)
         lookup = (inflate ? "value" : "key")
         rs["rows"].collect { |r| r[lookup] }        
       end
       
       def self.cdb_all_checksums(couchdb = nil)
         rs = (couchdb || Chef::CouchDB.new).list("checksums", true)
         rs["rows"].inject({}) { |hash_result, r| hash_result[r['key']] = 1; hash_result }
       end
   
       def self.cdb_load(checksum, couchdb=nil)
         # Probably want to look for a view here at some point
         (couchdb || Chef::CouchDB.new).load("checksum", checksum)
       end
   
       def cdb_destroy(couchdb=nil)
         (couchdb || Chef::CouchDB.new).delete("checksum", checksum, @couchdb_rev)
       end
   
       def cdb_save(couchdb=nil)
         @couchdb_rev = (couchdb || Chef::CouchDB.new).store("checksum", checksum, self)["rev"]
       end
   
   
       private
   
       # Deletes the file backing this checksum from the on-disk repo.
       # Purging the checksums is how users can get back to a valid state if
       # they've deleted files, so we silently swallow Errno::ENOENT here.
       def purge_file
         FileUtils.rm(file_location)
       rescue Errno::ENOENT
         true
       end
   
     end
   end

lib/chef/knife/cookbook_site_show.rb

   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteShow < Knife
   
         banner "knife cookbook site show COOKBOOK [VERSION] (options)"
         category "cookbook site"
   
         def run
           case @name_args.length
           when 1 
             cookbook_data = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}")
           when 2
             cookbook_data = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].gsub('.', '_')}")
           end
           output(format_for_display(cookbook_data))
         end
   
         def get_cookbook_list(items=10, start=0, cookbook_collection={})
           cookbooks_url = "http://cookbooks.opscode.com/api/v1/cookbooks?items=#{items}&start=#{start}"
           cr = noauth_rest.get_rest(cookbooks_url)
           cr["items"].each do |cookbook|
             cookbook_collection[cookbook["cookbook_name"]] = cookbook
           end
           new_start = start + cr["items"].length
           if new_start < cr["total"]
             get_cookbook_list(items, new_start, cookbook_collection) 
           else
             cookbook_collection
           end
         end
       end
     end
   end
   
   
   
   
   

lib/chef/knife/tag_create.rb

   #
   # Author:: Ryan Davis ()
   # Author:: Daniel DeLeo ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2011 Ryan Davis and Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class TagCreate < Knife
   
         deps do
           require 'chef/node'
         end
   
         banner "knife tag create NODE TAG ..."
   
         def run
           name = @name_args[0]
           tags = @name_args[1..-1]
   
           if name.nil? || tags.nil? || tags.empty?
             show_usage
             ui.fatal("You must specify a node name and at least one tag.")
             exit 1
           end
   
           node = Chef::Node.load name
           tags.each do |tag|
             (node.tags << tag).uniq!
           end
           node.save
           ui.info("Created tags #{tags.join(", ")} for node #{name}.")
         end
       end
     end
   end

lib/chef/knife/data_bag_from_file.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class DataBagFromFile < Knife
   
         deps do
           require 'chef/data_bag'
           require 'chef/data_bag_item'
           require 'chef/knife/core/object_loader'
           require 'chef/json_compat'
           require 'chef/encrypted_data_bag_item'
         end
   
         banner "knife data bag from file BAG FILE (options)"
         category "data bag"
   
         option :secret,
         :short => "-s SECRET",
         :long  => "--secret ",
         :description => "The secret key to use to encrypt data bag item values"
   
         option :secret_file,
         :long => "--secret-file SECRET_FILE",
         :description => "A file containing the secret key to use to encrypt data bag item values"
   
         def read_secret
           if config[:secret]
             config[:secret]
           else
             Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
           end
         end
   
         def use_encryption
           if config[:secret] && config[:secret_file]
             ui.fatal("please specify only one of --secret, --secret-file")
             exit(1)
           end
           config[:secret] || config[:secret_file]
         end
   
         def loader
           @loader ||= Knife::Core::ObjectLoader.new(DataBagItem, ui)
         end
   
         def run
           if @name_args.size != 2
             ui.msg(opt_parser)
             exit(1)
           end
           @data_bag, @item_path = @name_args[0], @name_args[1]
           item = loader.load_from("data_bags", @data_bag, @item_path)
           item = if use_encryption
                    secret = read_secret
                    Chef::EncryptedDataBagItem.encrypt_data_bag_item(item, secret)
                  else
                    item
                  end
           dbag = Chef::DataBagItem.new
           dbag.data_bag(@name_args[0])
           dbag.raw_data = item
           dbag.save
           ui.info("Updated data_bag_item[#{dbag.data_bag}::#{dbag.id}]")
         end
       end
     end
   end

lib/chef/mixin/convert_to_class_name.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module Mixin
       module ConvertToClassName
         extend self
   
         def convert_to_class_name(str)
           rname = nil
           regexp = %r{^(.+?)(_(.+))?$}
           
           mn = str.match(regexp)
           if mn
             rname = mn[1].capitalize
   
             while mn && mn[3]
               mn = mn[3].match(regexp)          
               rname << mn[1].capitalize if mn
             end
           end
   
           rname
         end
         
         def convert_to_snake_case(str, namespace=nil)
           str = str.dup
           str.sub!(/^#{namespace}(\:\:)?/, '') if namespace
           str.gsub!(/[A-Z]/) {|s| "_" + s}
           str.downcase!
           str.sub!(/^\_/, "")
           str
         end
         
         def snake_case_basename(str)
           with_namespace = convert_to_snake_case(str)
           with_namespace.split("::").last.sub(/^_/, '')
         end
         
         def filename_to_qualified_string(base, filename)
           file_base = File.basename(filename, ".rb")
           base.to_s + (file_base == 'default' ? '' : "_#{file_base}")
         end
         
       end
     end
   end

lib/chef/provider/deploy/revision.rb

   #
   # Author:: Daniel DeLeo ()
   # Author:: Tim Hinderliter ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider'
   require 'chef/provider/deploy'
   require 'chef/json_compat'
   
   class Chef
     class Provider
       class Deploy
         class Revision < Chef::Provider::Deploy
   
           def all_releases
             sorted_releases
           end
   
           protected
   
           def release_created(release)
             sorted_releases {|r| r.delete(release); r << release }
           end
   
           def release_deleted(release)
             sorted_releases { |r| r.delete(release)}
           end
   
           def release_slug
             scm_provider.revision_slug
           end
   
           private
   
           def sorted_releases
             cache = load_cache
             if block_given?
               yield cache
               save_cache(cache)
             end
             cache
           end
   
           def sorted_releases_from_filesystem
             Dir.glob(new_resource.deploy_to + "/releases/*").sort_by { |d| ::File.ctime(d) }
           end
   
           def load_cache
             begin
               Chef::JSONCompat.from_json(Chef::FileCache.load("revision-deploys/#{new_resource.name}"))
             rescue Chef::Exceptions::FileNotFound
               sorted_releases_from_filesystem
             end
           end
   
           def save_cache(cache)
             Chef::FileCache.store("revision-deploys/#{new_resource.name}", cache.to_json)
             cache
           end
   
         end
       end
     end
   end

lib/chef/run_list/run_list_expansion.rb

   #
   # Author:: Daniel DeLeo ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010, 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/mash'
   
   require 'chef/mixin/deep_merge'
   
   require 'chef/role'
   require 'chef/couchdb'
   require 'chef/rest'
   
   class Chef
     class RunList
       # Abstract Base class for expanding a run list. Subclasses must handle
       # fetching roles from a data source by defining +fetch_role+
       class RunListExpansion
   
         attr_reader :run_list_items
   
         # A VersionedRecipeList of recipes. Populated only after #expand
         # is called.
         attr_reader :recipes
   
         attr_reader :default_attrs
   
         attr_reader :override_attrs
   
         attr_reader :errors
   
         attr_reader :environment
   
         # The data source passed to the constructor. Not used in this class.
         # In subclasses, this is a couchdb or Chef::REST object pre-configured
         # to fetch roles from their correct location.
         attr_reader :source
   
         def initialize(environment, run_list_items, source=nil)
           @environment = environment
           @errors = Array.new
   
           @run_list_items = run_list_items.dup
           @source = source
   
           @default_attrs = Mash.new
           @override_attrs = Mash.new
   
           @recipes = Chef::RunList::VersionedRecipeList.new
   
           @applied_roles = {}
         end
   
         # Did we find any errors (expanding roles)?
         def errors?
           @errors.length > 0
         end
   
         alias :invalid? :errors?
   
         # Recurses over the run list items, expanding roles. After this,
         # +recipes+ will contain the fully expanded recipe list
         def expand
           # Sure do miss function arity when being recursive
           expand_run_list_items(@run_list_items)
         end
   
         # Fetches and inflates a role
         # === Returns
         # Chef::Role  in most cases
         # false       if the role has already been applied
         # nil         if the role does not exist
         def inflate_role(role_name)
           return false if applied_role?(role_name) # Prevent infinite loops
           applied_role(role_name)
           fetch_role(role_name)
         end
   
         def apply_role_attributes(role)
           @default_attrs = Chef::Mixin::DeepMerge.merge(@default_attrs, role.default_attributes)
           @override_attrs = Chef::Mixin::DeepMerge.merge(@override_attrs, role.override_attributes)
         end
   
         def applied_role?(role_name)
           @applied_roles.has_key?(role_name)
         end
   
         # Returns an array of role names that were expanded; this
         # includes any roles that were in the original, pre-expansion
         # run_list as well as roles processed during
         # expansion. Populated only after #expand is called.
         def roles
           @applied_roles.keys
         end
   
         # In subclasses, this method will fetch the role from the data source.
         def fetch_role(name)
           raise NotImplementedError
         end
   
         # When a role is not found, an error message is logged, but no
         # exception is raised.  We do add an entry in the errors collection.
         # === Returns
         # nil
         def role_not_found(name)
           Chef::Log.error("Role #{name} is in the runlist but does not exist. Skipping expand.")
           @errors << name
           nil
         end
   
         private
   
         # these methods modifies internal state based on arguments, so hide it.
   
         def applied_role(role_name)
           @applied_roles[role_name] = true
         end
   
         def expand_run_list_items(items)
           if entry = items.shift
             case entry.type
             when :recipe
               recipes.add_recipe(entry.name, entry.version)
             when :role
               if role = inflate_role(entry.name)
                 expand_run_list_items(role.run_list_for(@environment).run_list_items)
                 apply_role_attributes(role)
               end
             end
             expand_run_list_items(items)
           end
         end
   
       end
   
       # Expand a run list from disk. Suitable for chef-solo
       class RunListExpansionFromDisk < RunListExpansion
   
         def fetch_role(name)
           Chef::Role.from_disk(name)
         rescue Chef::Exceptions::RoleNotFound
           role_not_found(name)
         end
   
       end
   
       # Expand a run list from the chef-server API.
       class RunListExpansionFromAPI < RunListExpansion
   
         def rest
           @rest ||= (source || Chef::REST.new(Chef::Config[:role_url]))
         end
   
         def fetch_role(name)
           rest.get_rest("roles/#{name}")
         rescue Net::HTTPServerException => e
           if e.message == '404 "Not Found"'
             role_not_found(name)
           else
             raise
           end
         end
       end
   
       # Expand a run list from couchdb. Used in chef-server-api
       class RunListExpansionFromCouchDB < RunListExpansion
   
         def couchdb
           source
         end
   
         def fetch_role(name)
           Chef::Role.cdb_load(name, couchdb)
         rescue Chef::Exceptions::CouchDBNotFound
           role_not_found(name)
         end
   
       end
     end
   end

lib/chef/provider/service/insserv.rb

   #
   # Author:: Bryan McLellan 
   # Copyright:: Copyright (c) 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/init'
   require 'chef/mixin/command'
   
   class Chef
     class Provider
       class Service
         class Insserv < Chef::Provider::Service::Init
   
           def load_current_resource
             super
   
             # Look for a /etc/rc.*/SnnSERVICE link to signifiy that the service would be started in a runlevel
             if Dir.glob("/etc/rc**/S*#{@current_resource.service_name}").empty?
               @current_resource.enabled false
             else
               @current_resource.enabled true 
             end
     
             @current_resource
           end
   
           def enable_service()
             run_command(:command => "/sbin/insserv -r -f #{@new_resource.service_name}")
             run_command(:command => "/sbin/insserv -d -f #{@new_resource.service_name}")
           end
   
           def disable_service()
             run_command(:command => "/sbin/insserv -r -f #{@new_resource.service_name}")
           end
         end
       end
     end
   end

lib/chef/provider/service/redhat.rb

   #
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/service'
   require 'chef/provider/service/init'
   require 'chef/mixin/shell_out'
   
   class Chef
     class Provider
       class Service
         class Redhat < Chef::Provider::Service::Init
           include Chef::Mixin::ShellOut
           
           CHKCONFIG_ON = /\d:on/
           
           def initialize(new_resource, run_context)
             super
              @init_command = "/sbin/service #{@new_resource.service_name}"
              @new_resource.supports[:status] = true
            end
           
           def load_current_resource
             unless ::File.exists? "/sbin/chkconfig"
               raise Chef::Exceptions::Service, "/sbin/chkconfig does not exist!"
             end
             
             super
             
             chkconfig = shell_out!("/sbin/chkconfig --list #{@current_resource.service_name}", :returns => [0,1])
             @current_resource.enabled(  (chkconfig.stdout =~ CHKCONFIG_ON))
             @current_resource        
           end
   
           def enable_service()
             shell_out! "/sbin/chkconfig #{@new_resource.service_name} on"
           end
   
           def disable_service()
             shell_out! "/sbin/chkconfig #{@new_resource.service_name} off"
           end
           
         end
       end
     end
   end

lib/chef/knife/node_run_list_remove.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeRunListRemove < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node run_list remove [NODE] [ENTRY] (options)"
   
         def run
           node = Chef::Node.load(@name_args[0])
           entry = @name_args[1]
   
           node.run_list.remove(entry)
   
           node.save
   
           config[:run_list] = true
   
           output(format_for_display(node))
         end
   
       end
     end
   end
   

lib/chef/knife/data_bag_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class DataBagDelete < Knife
   
         deps do
           require 'chef/data_bag'
         end
   
         banner "knife data bag delete BAG [ITEM] (options)"
         category "data bag"
   
         def run 
           if @name_args.length == 2
             delete_object(Chef::DataBagItem, @name_args[1], "data_bag_item") do
               rest.delete_rest("data/#{@name_args[0]}/#{@name_args[1]}")
             end
           elsif @name_args.length == 1
             delete_object(Chef::DataBag, @name_args[0], "data_bag") do
               rest.delete_rest("data/#{@name_args[0]}")
             end
           else
             show_usage
             ui.fatal("You must specify at least a data bag name")
             exit 1
           end
         end
       end
     end
   end
   
   

lib/chef/knife/cookbook_site_list.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteList < Knife
   
         banner "knife cookbook site list (options)"
         category "cookbook site"
   
         option :with_uri,
           :short => "-w",
           :long => "--with-uri",
           :description => "Show corresponding URIs"
   
         def run
           if config[:with_uri]
             cookbooks = Hash.new
             get_cookbook_list.each{ |k,v| cookbooks[k] = v['cookbook'] }
             ui.output(format_for_display(cookbooks))
           else
             ui.msg(ui.list(get_cookbook_list.keys.sort, :columns_down))
           end
         end
   
         def get_cookbook_list(items=10, start=0, cookbook_collection={})
           cookbooks_url = "http://cookbooks.opscode.com/api/v1/cookbooks?items=#{items}&start=#{start}"
           cr = noauth_rest.get_rest(cookbooks_url)
           cr["items"].each do |cookbook|
             cookbook_collection[cookbook["cookbook_name"]] = cookbook
           end
           new_start = start + cr["items"].length
           if new_start < cr["total"]
             get_cookbook_list(items, new_start, cookbook_collection)
           else
             cookbook_collection
           end
         end
       end
     end
   end
   
   
   
   

lib/chef/solr_query/query_transform.rb

   #
   # Author:: Seth Falcon ()
   # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'treetop'
   require 'chef/solr_query/lucene_nodes'
   
   class Chef
     class Exceptions
       class QueryParseError < StandardError
       end
     end
   end
   
   class Chef
     class SolrQuery
       class QueryTransform
         @@base_path = File.expand_path(File.dirname(__FILE__))
         Treetop.load(File.join(@@base_path, 'lucene.treetop'))
         @@parser = LuceneParser.new
   
         def self.parse(data)
           tree = @@parser.parse(data)
           msg = "Parse error at offset: #{@@parser.index}\n"
           msg += "Reason: #{@@parser.failure_reason}"
           raise Chef::Exceptions::QueryParseError, msg if tree.nil?
           self.clean_tree(tree)
           tree.to_array
         end
   
         def self.transform(data)
           return "*:*" if data == "*:*"
           tree = @@parser.parse(data)
           msg = "Parse error at offset: #{@@parser.index}\n"
           msg += "Reason: #{@@parser.failure_reason}"
           raise Chef::Exceptions::QueryParseError, msg if tree.nil?
           self.clean_tree(tree)
           tree.transform
         end
   
         private
   
         def self.clean_tree(root_node)
           return if root_node.elements.nil?
           root_node.elements.delete_if do |node|
             node.class.name == "Treetop::Runtime::SyntaxNode"
           end
           root_node.elements.each { |node| self.clean_tree(node) }
         end
       end
     end
   end

lib/chef/resource/ohai.rb

   #
   # Author:: Michael Leinartas ()
   # Copyright:: Copyright (c) 2010 Michael Leinartas
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Resource
       class Ohai < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :ohai
           @allowed_actions.push(:reload)
           @action = :reload
           @plugin = nil
         end
   
         def plugin(arg=nil)
           set_or_return(
             :plugin,
             arg,
             :kind_of => [ String ]
           )
         end
       end
     end
   end

lib/chef/knife/exec.rb

   #--
   # Author:: Daniel DeLeo (
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef::Knife::Exec < Chef::Knife
   
     banner "knife exec [SCRIPT] (options)"
   
     option :exec,
       :short => "-E CODE",
       :long => "--exec CODE",
       :description => "a string of Chef code to execute"
   
     deps do
       require 'chef/shef/ext'
     end
   
     def run
       scripts = Array(name_args)
       context = Object.new
       Shef::Extensions.extend_context_object(context)
       if config[:exec]
         context.instance_eval(config[:exec], "-E Argument", 0)
       elsif !scripts.empty?
         scripts.each do |script|
           file = File.expand_path(script)
           context.instance_eval(IO.read(file), file, 0)
         end
       else
         script = STDIN.read
         context.instance_eval(script, "STDIN", 0)
       end
     end
     
   end

lib/chef/knife/client_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientDelete < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         banner "knife client delete CLIENT (options)"
   
         def run
           @client_name = @name_args[0]
   
           if @client_name.nil?
             show_usage
             ui.fatal("You must specify a client name")
             exit 1
           end
   
           delete_object(Chef::ApiClient, @client_name)
         end
   
       end
     end
   end

lib/chef/knife/environment_show.rb

   #
   # Author:: Stephen Delano ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class EnvironmentShow < Knife
   
         deps do
           require 'chef/environment'
           require 'chef/json_compat'
         end
   
         banner "knife environment show ENVIRONMENT (options)"
   
         def run
           env_name = @name_args[0]
   
           if env_name.nil?
             show_usage
             ui.fatal("You must specify an environment name")
             exit 1
           end
   
           env = Chef::Environment.load(env_name)
           output(format_for_display(env))
         end
       end
     end
   end

lib/chef/provider/ohai.rb

   #
   # Author:: Michael Leianrtas ()
   # Copyright:: Copyright (c) 2010 Michael Leinartas
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'ohai'
   
   class Chef
     class Provider
       class Ohai < Chef::Provider
   
         def load_current_resource
           true
         end
   
         def action_reload
           ohai = ::Ohai::System.new
           if @new_resource.plugin
             ohai.require_plugin @new_resource.plugin
           else
             ohai.all_plugins
           end
           node.automatic_attrs.merge! ohai.data
           Chef::Log.info("#{@new_resource} reloaded")
           @new_resource.updated_by_last_action(true)
         end
       end
     end
   end

lib/chef/knife/environment_create.rb

   #
   # Author:: Stephen Delano ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class EnvironmentCreate < Knife
   
         deps do
           require 'chef/environment'
           require 'chef/json_compat'
         end
   
         banner "knife environment create ENVIRONMENT (options)"
   
         option :description,
           :short => "-d DESCRIPTION",
           :long => "--description DESCRIPTION",
           :description => "The environment description"
   
         def run
           env_name = @name_args[0]
   
           if env_name.nil?
             show_usage
             ui.fatal("You must specify an environment name")
             exit 1
           end
   
           env = Chef::Environment.new
           env.name(env_name)
           env.description(config[:description]) if config[:description]
           create_object(env)
         end
       end
     end
   end

lib/chef/knife/environment_delete.rb

   #
   # Author:: Stephen Delano ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class EnvironmentDelete < Knife
   
         deps do
           require 'chef/environment'
           require 'chef/json_compat'
         end
   
         banner "knife environment delete ENVIRONMENT (options)"
   
         def run
           env_name = @name_args[0]
   
           if env_name.nil?
             show_usage
             ui.fatal("You must specify an environment name")
             exit 1
           end
   
           delete_object(Chef::Environment, env_name)
         end
       end
     end
   end

lib/chef/knife/environment_edit.rb

   #
   # Author:: Stephen Delano ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class EnvironmentEdit < Knife
   
         deps do
           require 'chef/environment'
           require 'chef/json_compat'
         end
   
         banner "knife environment edit ENVIRONMENT (options)"
   
         def run
           env_name = @name_args[0]
   
           if env_name.nil?
             show_usage
             ui.fatal("You must specify an environment name")
             exit 1
           end
   
           edit_object(Chef::Environment, env_name)
         end
       end
     end
   end

lib/chef/knife/role_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleDelete < Knife
   
         deps do
           require 'chef/role'
           require 'chef/json_compat'
         end
   
         banner "knife role delete ROLE (options)"
   
         def run
           @role_name = @name_args[0]
   
           if @role_name.nil?
             show_usage
             ui.fatal("You must specify a role name")
             exit 1
           end
   
           delete_object(Chef::Role, @role_name)
         end
   
       end
     end
   end
   

lib/chef/knife/node_delete.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeDelete < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node delete NODE (options)"
   
         def run
           @node_name = @name_args[0]
   
           if @node_name.nil?
             show_usage
             ui.fatal("You must specify a node name")
             exit 1
           end
   
           delete_object(Chef::Node, @node_name)
         end
   
       end
     end
   end
   

lib/chef/knife/node_create.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeCreate < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node create NODE (options)"
   
         def run
           @node_name = @name_args[0]
   
           if @node_name.nil?
             show_usage
             ui.fatal("You must specify a node name")
             exit 1
           end
   
           node = Chef::Node.new
           node.name(@node_name)
           create_object(node)
         end
       end
     end
   end
   
   
   

lib/chef/knife/client_edit.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientEdit < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         banner "knife client edit CLIENT (options)"
   
         def run
           @client_name = @name_args[0]
   
           if @client_name.nil?
             show_usage
             ui.fatal("You must specify a client name")
             exit 1
           end
   
           edit_object(Chef::ApiClient, @client_name)
         end
       end
     end
   end

lib/chef/knife/client_show.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class ClientShow < Knife
   
         deps do
           require 'chef/api_client'
           require 'chef/json_compat'
         end
   
         banner "knife client show CLIENT (options)"
   
         option :attribute,
           :short => "-a ATTR",
           :long => "--attribute ATTR",
           :description => "Show only one attribute"
   
         def run
           @client_name = @name_args[0]
   
           if @client_name.nil?
             show_usage
             ui.fatal("You must specify a client name")
             exit 1
           end
   
           client = Chef::ApiClient.load(@client_name)
           output(format_for_display(client))
         end
   
       end
     end
   end

lib/chef/search/query.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/config'
   require 'uri'
   require 'chef/rest'
   require 'chef/node'
   require 'chef/role'
   require 'chef/data_bag'
   require 'chef/data_bag_item'
   
   class Chef
     class Search
       class Query
   
         attr_accessor :rest
   
         def initialize(url=nil)
           @rest = Chef::REST.new(url ||Chef::Config[:search_url])
         end
   
         # Search Solr for objects of a given type, for a given query. If you give
         # it a block, it will handle the paging for you dynamically.
         def search(type, query="*:*", sort='X_CHEF_id_CHEF_X asc', start=0, rows=1000, &block)
           raise ArgumentError, "Type must be a string or a symbol!" unless (type.kind_of?(String) || type.kind_of?(Symbol))
   
           response = @rest.get_rest("search/#{type}?q=#{escape(query)}&sort=#{escape(sort)}&start=#{escape(start)}&rows=#{escape(rows)}")
           if block
             response["rows"].each { |o| block.call(o) unless o.nil?}
             unless (response["start"] + response["rows"].length) >= response["total"]
               nstart = response["start"] + rows
               search(type, query, sort, nstart, rows, &block)
             end
             true
           else
             [ response["rows"], response["start"], response["total"] ]
           end
         end
   
         def list_indexes
           response = @rest.get_rest("search")
         end
   
         private
           def escape(s)
             s && URI.escape(s.to_s)
           end
       end
     end
   end

lib/chef/resource/ruby_block.rb

   #
   # Author:: Adam Jacob ()
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Resource
       class RubyBlock < Chef::Resource
         
         def initialize(name, run_context=nil)
           super
           @resource_name = :ruby_block
           @action = "create"
           @allowed_actions.push(:create)
         end
   
         def block(&block)
           if block_given? and block
             @block = block
           else
             @block
           end
         end
       end
     end
   end

lib/chef/knife/role_create.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleCreate < Knife
   
         deps do
           require 'chef/role'
           require 'chef/json_compat'
         end
   
         banner "knife role create ROLE (options)"
   
         option :description,
           :short => "-d DESC",
           :long => "--description DESC",
           :description => "The role description"
   
         def run
           @role_name = @name_args[0]
   
           if @role_name.nil?
             show_usage
             ui.fatal("You must specify a role name")
             exit 1
           end
   
           role = Chef::Role.new
           role.name(@role_name)
           role.description(config[:description]) if config[:description]
           create_object(role)
         end
       end
     end
   end
   
   

lib/chef/knife/tag_list.rb

   #
   # Author:: Ryan Davis ()
   # Author:: Daniel DeLeo ()
   # Author:: Nuo Yan ()
   # Copyright:: Copyright (c) 2011 Ryan Davis and Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class TagList < Knife
   
         deps do
           require 'chef/node'
         end
   
         banner "knife tag list NODE"
   
         def run
           name = @name_args[0]
   
           if name.nil?
             show_usage
             ui.fatal("You must specify a node name.")
             exit 1
           end
   
           node = Chef::Node.load(name)
           output(node.tags)
         end
       end
     end
   end

lib/chef/mixin/shell_out.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/shell_out'
   require 'chef/config'
   
   class Chef
     module Mixin
       module ShellOut
   
         def shell_out(*command_args)
           cmd = Chef::ShellOut.new(*command_args)
           if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.debug?
             cmd.live_stream = STDOUT
           end
           cmd.run_command
           cmd
         end
   
         def shell_out!(*command_args)
           cmd= shell_out(*command_args)
           cmd.error!
           cmd
         end
       end
     end
   end

lib/chef/knife/environment_from_file.rb

   #
   # Author:: Stephen Delano ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Knife
       class EnvironmentFromFile < Knife
   
         deps do
           require 'chef/environment'
           require 'chef/knife/core/object_loader'
         end
   
         banner "knife environment from file FILE (options)"
   
         def loader
           @loader ||= Knife::Core::ObjectLoader.new(Chef::Environment, ui)
         end
   
   
         def run
           if @name_args[0].nil?
             show_usage
             ui.fatal("You must specify a file to load")
             exit 1
           end
   
   
           updated = loader.load_from("environments", @name_args[0])
           updated.save
           output(format_for_display(updated)) if config[:print_after]
           ui.info("Updated Environment #{updated.name}")
         end
       end
     end
   end

lib/chef/mixin/deprecation.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module Mixin
       module Deprecation
         class DeprecatedObjectProxyBase
           KEEPERS = %w{__id__ __send__ instance_eval == equal? initialize object_id}
           instance_methods.each { |method_name| undef_method(method_name) unless KEEPERS.include?(method_name.to_s)}
         end
   
         class DeprecatedInstanceVariable < DeprecatedObjectProxyBase
           def initialize(target, ivar_name, level=nil)
             @target, @ivar_name = target, ivar_name
             @level ||= :warn
           end
   
           def method_missing(method_name, *args, &block)
             log_deprecation_msg(caller[0..3])
             @target.send(method_name, *args, &block)
           end
   
           def inspect
             @target.inspect
           end
   
           private
   
           def log_deprecation_msg(*called_from)
             called_from = called_from.flatten
             log("Accessing #{@ivar_name} by the variable @#{@ivar_name} is deprecated. Support will be removed in a future release.")
             log("Please update your cookbooks to use #{@ivar_name} in place of @#{@ivar_name}. Accessed from:")
             called_from.each {|l| log(l)}
           end
   
           def log(msg)
             # WTF: I don't get the log prefix (i.e., "[timestamp] LEVEL:") if I
             # send to Chef::Log. No one but me should use method_missing, ever.
             Chef::Log.logger.send(@level, msg)
           end
   
         end
   
         def deprecated_ivar(obj, name, level=nil)
           DeprecatedInstanceVariable.new(obj, name, level)
         end
   
       end
     end
   end

lib/chef/shell_out.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'etc'
   require 'tmpdir'
   require 'chef/log'
   require 'fcntl'
   require 'chef/exceptions'
   require 'chef/shell_out/unix'
   
   class Chef
   
     # == Chef::ShellOut
     # Provides a simplified interface to shelling out yet still collecting both
     # standard out and standard error and providing full control over environment,
     # working directory, uid, gid, etc.
     #
     # No means for passing input to the subprocess is provided, nor is there any
     # way to inspect the output of the command as it is being read. If you need
     # to do that, you have to use popen4 (in Chef::Mixin::Command)
     #
     # === Platform Support
     # Chef::ShellOut uses Kernel.fork() and is therefore unsuitable for Windows
     # or jruby.
     class ShellOut
       READ_WAIT_TIME = 0.01
       READ_SIZE = 4096
       DEFAULT_READ_TIMEOUT = 600
       DEFAULT_ENVIRONMENT = {'LC_ALL' => 'C'}
   
       if RUBY_PLATFORM =~ /mswin|mingw32|windows/
         require 'chef/shell_out/windows'
         include ShellOut::Windows
       else
         require 'chef/shell_out/unix'
         include ShellOut::Unix
       end
   
       attr_accessor :user
       attr_accessor :group
       attr_accessor :cwd
       attr_accessor :valid_exit_codes
       attr_accessor :live_stream
       attr_accessor :command_log_level
       attr_accessor :command_log_prepend
   
       attr_reader :command, :umask, :environment
       attr_writer :timeout
       attr_reader :execution_time
   
       attr_reader :stdout, :stderr, :status
   
       attr_reader :stdin_pipe, :stdout_pipe, :stderr_pipe, :process_status_pipe
   
       # === Arguments:
       # Takes a single command, or a list of command fragments. These are used
       # as arguments to Kernel.exec. See the Kernel.exec documentation for more
       # explanation of how arguments are evaluated. The last argument can be an
       # options Hash.
       # === Options:
       # If the last argument is a Hash, it is removed from the list of args passed
       # to exec and used as an options hash. The following options are available:
       # * +user+: the user the commmand should run as. if an integer is given, it is
       #   used as a uid. A string is treated as a username and resolved to a uid
       #   with Etc.getpwnam
       # * +group+: the group the command should run as. works similarly to +user+
       # * +cwd+: the directory to chdir to before running the command
       # * +umask+: a umask to set before running the command. If given as an Integer,
       #   be sure to use two leading zeros so it's parsed as Octal. A string will
       #   be treated as an octal integer
       # * +returns+:  one or more Integer values to use as valid exit codes for the
       #   subprocess. This only has an effect if you call +error!+ after
       #   +run_command+.
       # * +environment+: a Hash of environment variables to set before the command
       #   is run. By default, the environment will *always* be set to 'LC_ALL' => 'C'
       #   to prevent issues with multibyte characters in Ruby 1.8. To avoid this,
       #   use :environment => nil for *no* extra environment settings, or
       #   :environment => {'LC_ALL'=>nil, ...} to set other environment settings
       #   without changing the locale.
       # * +timeout+: a Numeric value for the number of seconds to wait on the
       #   child process before raising an Exception. This is calculated as the
       #   total amount of time that ShellOut waited on the child process without
       #   receiving any output (i.e., IO.select returned nil). Default is 60
       #   seconds. Note: the stdlib Timeout library is not used.
       # === Examples:
       # Invoke find(1) to search for .rb files:
       #   find = Chef::ShellOut.new("find . -name '*.rb'")
       #   find.run_command
       #   # If all went well, the results are on +stdout+
       #   puts find.stdout
       #   # find(1) prints diagnostic info to STDERR:
       #   puts "error messages" + find.stderr
       #   # Raise an exception if it didn't exit with 0
       #   find.error!
       # Run a command as the +www+ user with no extra ENV settings from +/tmp+
       #   cmd = Chef::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
       #   cmd.run_command # etc.
       def initialize(*command_args)
         @stdout, @stderr = '', ''
         @live_stream = nil
         @command_log_level = :debug
         @command_log_prepend = nil 
         @environment = DEFAULT_ENVIRONMENT
         @cwd = nil
         @valid_exit_codes = [0]
   
         if command_args.last.is_a?(Hash)
           parse_options(command_args.pop)
         end
   
         @command = command_args.size == 1 ? command_args.first : command_args
       end
   
       def umask=(new_umask)
         @umask = (new_umask.respond_to?(:oct) ? new_umask.oct : new_umask.to_i) & 007777
       end
   
       def uid
         return nil unless user
         user.kind_of?(Integer) ? user : Etc.getpwnam(user.to_s).uid
       end
   
       def gid
         return nil unless group
         group.kind_of?(Integer) ? group : Etc.getgrnam(group.to_s).gid
       end
   
       def timeout
         @timeout || DEFAULT_READ_TIMEOUT
       end
   
       # Creates a String showing the output of the command, including a banner
       # showing the exact command executed. Used by +invalid!+ to show command
       # results when the command exited with an unexpected status.
       def format_for_exception
         msg = ""
         msg << "---- Begin output of #{command} ----\n"
         msg << "STDOUT: #{stdout.strip}\n"
         msg << "STDERR: #{stderr.strip}\n"
         msg << "---- End output of #{command} ----\n"
         msg << "Ran #{command} returned #{status.exitstatus}" if status
         msg
       end
   
       def exitstatus
         @status && @status.exitstatus
       end
   
       # Run the command, writing the command's standard out and standard error
       # to +stdout+ and +stderr+, and saving its exit status object to +status+
       # === Returns
       # returns   +self+; +stdout+, +stderr+, +status+, and +exitstatus+ will be
       # populated with results of the command
       # === Raises
       # * Errno::EACCES  when you are not privileged to execute the command
       # * Errno::ENOENT  when the command is not available on the system (or not
       #   in the current $PATH)
       # * Chef::Exceptions::CommandTimeout  when the command does not complete
       #   within +timeout+ seconds (default: 60s)
       def run_command
         if command_log_prepend
           Chef::Log.send(command_log_level, "#{command_log_prepend} sh(#{@command})")
         else
           Chef::Log.send(command_log_level, "sh(#{@command})")
         end
         super
       end
   
       # Checks the +exitstatus+ against the set of +valid_exit_codes+. If
       # +exitstatus+ is not in the list of +valid_exit_codes+, calls +invalid!+,
       # which raises an Exception.
       # === Returns
       # nil::: always returns nil when it does not raise
       # === Raises
       # Chef::Exceptions::ShellCommandFailed::: via +invalid!+
       def error!
         unless Array(valid_exit_codes).include?(exitstatus)
           invalid!("Expected process to exit with #{valid_exit_codes.inspect}, but received '#{exitstatus}'")
         end
       end
   
       # Raises a Chef::Exceptions::ShellCommandFailed exception, appending the
       # command's stdout, stderr, and exitstatus to the exception message.
       # === Arguments
       # +msg+:  A String to use as the basis of the exception message. The
       # default explanation is very generic, providing a more informative message
       # is highly encouraged.
       # === Raises
       # Chef::Exceptions::ShellCommandFailed  always
       def invalid!(msg=nil)
         msg ||= "Command produced unexpected results"
         raise Chef::Exceptions::ShellCommandFailed, msg + "\n" + format_for_exception
       end
   
       def inspect
         "<#{self.class.name}##{object_id}: command: '#@command' process_status: #{@status.inspect} " +
         "stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
         "environment: #{@environment.inspect} timeout: #{timeout} user: #@user group: #@group working_dir: #@cwd >"
       end
   
       private
   
       def parse_options(opts)
         opts.each do |option, setting|
           case option.to_s
           when 'cwd'
             self.cwd = setting
           when 'user'
             self.user = setting
           when 'group'
             self.group = setting
           when 'umask'
             self.umask = setting
           when 'timeout'
             self.timeout = setting
           when 'returns'
             self.valid_exit_codes = Array(setting)
           when 'live_stream'
             self.live_stream = setting
           when 'command_log_level'
             self.command_log_level = setting
           when 'command_log_prepend'
             self.command_log_prepend = setting
           when 'environment', 'env'
             # passing :environment => nil means don't set any new ENV vars
             @environment = setting.nil? ? {} : @environment.dup.merge!(setting)
           else
             raise Chef::Exceptions::InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
           end
         end
       end
   
   
     end
   end

lib/chef/knife/cookbook_site_search.rb

   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookSiteSearch < Knife
   
         banner "knife cookbook site search QUERY (options)"
         category "cookbook site"
   
         def run
           output(search_cookbook(name_args[0]))
         end
   
         def search_cookbook(query, items=10, start=0, cookbook_collection={})
           cookbooks_url = "http://cookbooks.opscode.com/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
           cr = noauth_rest.get_rest(cookbooks_url)
           cr["items"].each do |cookbook|
             cookbook_collection[cookbook["cookbook_name"]] = cookbook
           end
           new_start = start + cr["items"].length
           if new_start < cr["total"]
             search_cookbook(query, items, new_start, cookbook_collection) 
           else
             cookbook_collection
           end
         end
       end
     end
   end
   
   
   
   
   

lib/chef/knife/role_edit.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleEdit < Knife
   
         deps do
           require 'chef/role'
           require 'chef/json_compat'
         end
   
         banner "knife role edit ROLE (options)"
   
         def run
           @role_name = @name_args[0]
   
           if @role_name.nil?
             show_usage
             ui.fatal("You must specify a role name")
             exit 1
           end
   
           ui.edit_object(Chef::Role, @role_name)
         end
       end
     end
   end
   
   
   

lib/chef/knife/role_show.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleShow < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife role show ROLE (options)"
   
         option :attribute,
           :short => "-a ATTR",
           :long => "--attribute ATTR",
           :description => "Show only one attribute"
   
         def run
           @role_name = @name_args[0]
   
           if @role_name.nil?
             show_usage
             ui.fatal("You must specify a role name")
             exit 1
           end
   
           role = Chef::Role.load(@role_name)
           output(format_for_display(config[:environment] ? role.environment(config[:environment]) : role))
         end
   
       end
     end
   end
   
   

lib/chef/resource/cookbook_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/file'
   require 'chef/provider/cookbook_file'
   
   class Chef
     class Resource
       class CookbookFile < Chef::Resource::File
         
         def initialize(name, run_context=nil)
           super
           @provider = Chef::Provider::CookbookFile
           @resource_name = :cookbook_file
           @action = "create"
           @source = ::File.basename(name)
           @cookbook = nil
         end
         
         def source(source_filename=nil)
           set_or_return(:source, source_filename, :kind_of => String)
         end
         
         def cookbook(cookbook_name=nil)
           set_or_return(:cookbook, cookbook_name, :kind_of => String)
         end
   
       end
     end
   end

lib/chef/mixin/check_helper.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     module Mixin
       module CheckHelper      
         def set_if_args(thing, arguments)
           raise ArgumentError, "Must call set_if_args with a block!" unless Kernel.block_given?
           if arguments != nil
             yield(arguments)
           else
             thing
           end
         end
       end
     end
   end

lib/chef/run_status.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   # == Chef::RunStatus
   # Tracks various aspects of a Chef run, including the Node and RunContext,
   # start and end time, and any Exception that stops the run. RunStatus objects
   # are passed to any notification or exception handlers at the completion of a
   # Chef run.
   class Chef::RunStatus
   
     attr_reader :run_context
   
     attr_writer :run_context
   
     attr_reader :start_time
   
     attr_reader :end_time
   
     attr_reader :exception
   
     attr_writer :exception
   
     def initialize(node)
       @node = node
     end
   
     def node
       @node
     end
   
     # sets +start_time+ to the current time.
     def start_clock
       @start_time = Time.now
     end
   
     # sets +end_time+ to the current time
     def stop_clock
       @end_time = Time.now
     end
   
     # The elapsed time between +start_time+ and +end_time+. Returns +nil+ if
     # either value is not set.
     def elapsed_time
       if @start_time && @end_time
         @end_time - @start_time
       else
         nil
       end
     end
   
     # The list of all resources in the current run context's +resource_collection+
     def all_resources
       @run_context && @run_context.resource_collection.all_resources
     end
   
     # The list of all resources in the current run context's +resource_collection+
     # that are marked as updated
     def updated_resources
       @run_context && @run_context.resource_collection.select { |r| r.updated }
     end
   
     # The backtrace from +exception+, if any
     def backtrace
       @exception && @exception.backtrace
     end
   
     # Did the Chef run fail?
     def failed?
       !success?
     end
   
     # Did the chef run succeed? returns +true+ if no exception has been set.
     def success?
       @exception.nil?
     end
   
     # A Hash representation of the RunStatus, with the following (Symbol) keys:
     # * :node
     # * :success
     # * :start_time
     # * :end_time
     # * :elapsed_time
     # * :all_resources
     # * :updated_resources
     # * :exception
     # * :backtrace
     def to_hash
       # use a flat hash here so we can't errors from intermediate values being nil
       { :node => node,
         :success => success?,
         :start_time => start_time,
         :end_time => end_time,
         :elapsed_time => elapsed_time,
         :all_resources => all_resources,
         :updated_resources => updated_resources,
         :exception => formatted_exception,
         :backtrace => backtrace}
     end
   
     # Returns a string of the format "ExceptionClass: message" or +nil+ if no
     # +exception+ is set.
     def formatted_exception
       @exception && "#{@exception.class.name}: #{@exception.message}"
     end
   
   end

lib/chef/knife/index_rebuild.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class IndexRebuild < Knife
   
         banner "knife index rebuild (options)"
         option :yes,
           :short        => "-y",
           :long         => "--yes",
           :boolean      => true,
           :description  => "don't bother to ask if I'm sure"
   
         def run
           nag
           output rest.post_rest("/search/reindex", {})
         end
   
         def nag
           unless config[:yes]
             yea_or_nay = ask_question("This operation is destructive. Rebuilding the index may take some time. You sure? (yes/no): ")
             unless yea_or_nay =~ /^y/i
               puts "aborting"
               exit 7
             end
           end
         end
   
   
       end
     end
   end

lib/chef/mixin/from_file.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     module Mixin
       module FromFile
       
         # Loads a given ruby file, and runs instance_eval against it in the context of the current 
         # object.  
         #
         # Raises an IOError if the file cannot be found, or is not readable.
         def from_file(filename)
           if File.exists?(filename) && File.readable?(filename)
             self.instance_eval(IO.read(filename), filename, 1)
           else
             raise IOError, "Cannot open or read #{filename}!"
           end
         end
   
         # Loads a given ruby file, and runs class_eval against it in the context of the current 
         # object.
         #
         # Raises an IOError if the file cannot be found, or is not readable.
         def class_from_file(filename)
           if File.exists?(filename) && File.readable?(filename)
             self.class_eval(IO.read(filename), filename, 1)
           else
             raise IOError, "Cannot open or read #{filename}!"
           end
         end
   
       end
     end
   end

lib/chef/recipe.rb

   #--
   # Author:: Adam Jacob ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   require 'chef/mixin/recipe_definition_dsl_core'
   require 'chef/mixin/from_file'
   require 'chef/mixin/language'
   require 'chef/mixin/language_include_recipe'
   
   require 'chef/mixin/deprecation'
   
   class Chef
     # == Chef::Recipe
     # A Recipe object is the context in which Chef recipes are evaluated.
     class Recipe
   
       include Chef::Mixin::FromFile
       include Chef::Mixin::Language
       include Chef::Mixin::LanguageIncludeRecipe
       include Chef::Mixin::RecipeDefinitionDSLCore
       include Chef::Mixin::Deprecation
   
       attr_accessor :cookbook_name, :recipe_name, :recipe, :params, :run_context
   
       # Parses a potentially fully-qualified recipe name into its
       # cookbook name and recipe short name.
       #
       # For example:
       #   "aws::elastic_ip" returns [:aws, "elastic_ip"]
       #   "aws" returns [:aws, "default"]
       #--
       # TODO: Duplicates functionality of RunListItem
       def self.parse_recipe_name(recipe_name)
         rmatch = recipe_name.match(/(.+?)::(.+)/)
         if rmatch
           [ rmatch[1].to_sym, rmatch[2] ]
         else
           [ recipe_name.to_sym, "default" ]
         end
       end
   
       def initialize(cookbook_name, recipe_name, run_context)
         @cookbook_name = cookbook_name
         @recipe_name = recipe_name
         @run_context = run_context
         # TODO: 5/19/2010 cw/tim: determine whether this can be removed
         @params = Hash.new
         @node = deprecated_ivar(run_context.node, :node, :warn)
       end
   
       # Used in DSL mixins
       def node
         run_context.node
       end
   
       # Used by the DSL to look up resources when executing in the context of a
       # recipe.
       def resources(*args)
         run_context.resource_collection.find(*args)
       end
   
       # Sets a tag, or list of tags, for this node.  Syntactic sugar for
       # run_context.node[:tags].
       #
       # With no arguments, returns the list of tags.
       #
       # === Parameters
       # tags:: A list of tags to add - can be a single string
       #
       # === Returns
       # tags:: The contents of run_context.node[:tags]
       def tag(*tags)
         if tags.length > 0
           tags.each do |tag|
             tag = tag.to_s
             run_context.node[:tags] << tag unless run_context.node[:tags].include?(tag)
           end
           run_context.node[:tags]
         else
           run_context.node[:tags]
         end
       end
   
       # Returns true if the node is tagged with *all* of the supplied +tags+.
       #
       # === Parameters
       # tags:: A list of tags
       #
       # === Returns
       # true:: If all the parameters are present
       # false:: If any of the parameters are missing
       def tagged?(*tags)
         tags.each do |tag|
           return false unless run_context.node[:tags].include?(tag)
         end
         true
       end
   
       # Removes the list of tags from the node.
       #
       # === Parameters
       # tags:: A list of tags
       #
       # === Returns
       # tags:: The current list of run_context.node[:tags]
       def untag(*tags)
         tags.each do |tag|
           run_context.node[:tags].delete(tag)
         end
       end
   
     end
   end

lib/chef/resource/subversion.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require "chef/resource/scm"
   
   class Chef
     class Resource
       class Subversion < Chef::Resource::Scm
         
         def initialize(name, run_context=nil)
           super
           @svn_arguments = '--no-auth-cache'
           @svn_info_args = '--no-auth-cache'
           @resource_name = :subversion
           @provider = Chef::Provider::Subversion
           allowed_actions << :force_export
         end
         
       end
     end
   end

lib/chef/knife/node_edit.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
   
       class NodeEdit < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
           require 'chef/knife/core/node_editor'
         end
   
         banner "knife node edit NODE (options)"
   
         option :all_attributes,
           :short => "-a",
           :long => "--all",
           :boolean => true,
           :description => "Display all attributes when editing"
   
         def run
           if node_name.nil?
             show_usage
             ui.fatal("You must specify a node name")
             exit 1
           end
   
           updated_node = node_editor.edit_node
           if updated_values = node_editor.updated?
             ui.info "Saving updated #{updated_values.join(', ')} on node #{node.name}"
             updated_node.save
           else
             ui.info "Node not updated, skipping node save"
           end
         end
   
         def node_name
           @node_name ||= @name_args[0]
         end
   
         def node_editor
           @node_editor ||= Knife::NodeEditor.new(node, ui, config)
         end
   
         def node
           @node ||= Chef::Node.load(node_name)
         end
   
       end
     end
   end
   
   

lib/chef/cookbook/file_system_file_vendor.rb

   #--
   # Author:: Christopher Walters ()
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/cookbook/file_vendor'
   
   class Chef
     class Cookbook
       # == Chef::Cookbook::FileSystemFileVendor
       # This FileVendor loads files from Chef::Config.cookbook_path. The
       # thing that's sort of janky about this FileVendor implementation is
       # that it basically takes only the cookbook's name from the manifest
       # and throws the rest away then re-builds the list of files on the
       # disk. This is due to the manifest not having the on-disk file
       # locations, since in the chef-client case, that information is
       # non-sensical.
       class FileSystemFileVendor < FileVendor
   
         def initialize(manifest, *repo_paths)
           @cookbook_name = manifest[:cookbook_name]
           @repo_paths = repo_paths.flatten
           raise ArgumentError, "You must specify at least one repo path" if @repo_paths.empty?
         end
   
         # Implements abstract base's requirement. It looks in the
         # Chef::Config.cookbook_path file hierarchy for the requested
         # file.
         def get_filename(filename)
           location = @repo_paths.inject(nil) do |memo, basepath|
             candidate_location = File.join(basepath, @cookbook_name, filename)
             memo = candidate_location if File.exist?(candidate_location)
             memo
           end
           raise "File #{filename} does not exist for cookbook #{@cookbook_name}" unless location
   
           location
         end
   
       end
     end
   end

lib/chef/resource/deploy_revision.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Daniel DeLeo
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Resource
       
       # Convenience class for using the deploy resource with the revision
       # deployment strategy (provider) 
       class DeployRevision < Chef::Resource::Deploy
         def initialize(*args, &block)
           super
           @resource_name = :deploy_revision
           @provider = Chef::Provider::Deploy::Revision
         end
       end
       
       class DeployBranch < Chef::Resource::DeployRevision
         def initialize(*args, &block)
           super
           @resource_name = :deploy_branch
         end
       end
       
     end
   end

lib/chef/knife/node_show.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   require 'chef/knife/core/node_presenter'
   
   class Chef
     class Knife
       class NodeShow < Knife
   
         include Knife::Core::NodeFormattingOptions
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
         end
   
         banner "knife node show NODE (options)"
   
         @attrs_to_show = []
         option :attribute,
           :short => "-a [ATTR]",
           :long => "--attribute [ATTR]",
           :proc => lambda {|val| @attrs_to_show << val},
           :description => "Show one or more attributes"
   
         option :run_list,
           :short => "-r",
           :long => "--run-list",
           :description => "Show only the run list"
   
         option :environment,
           :short        => "-E",
           :long         => "--environment",
           :description  => "Show only the Chef environment"
   
         def run
           ui.use_presenter Knife::Core::NodePresenter
           @node_name = @name_args[0]
   
           if @node_name.nil?
             show_usage
             ui.fatal("You must specify a node name")
             exit 1
           end
   
           node = Chef::Node.load(@node_name)
           output(format_for_display(node))
           self.class.attrs_to_show = []
         end
   
         def self.attrs_to_show=(attrs)
           @attrs_to_show = attrs
         end
       end
     end
   end
   

lib/chef/json_compat.rb

   #
   # Author:: Tim Hinderliter ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   # Wrapper class for interacting with JSON.
   
   require 'json'
   
   class Chef
     class JSONCompat
       JSON_MAX_NESTING = 1000
   
       class <
         # See CHEF-1292/PL-538. Increase the max nesting for JSON, which defaults
         # to 19, and isn't enough for some (for example, a Node within a Node)
         # structures.
         def opts_add_max_nesting(opts)
           if opts.nil? || !opts.has_key?(:max_nesting)
             opts = opts.nil? ? Hash.new : opts.clone
             opts[:max_nesting] = JSON_MAX_NESTING
           end
           opts
         end
   
         # Just call the JSON gem's parse method with a modified :max_nesting field
         def from_json(source, opts = {})
           ::JSON.parse(source, opts_add_max_nesting(opts))
         end
   
         def to_json(obj, opts = nil)
           obj.to_json(opts_add_max_nesting(opts))
         end
   
         def to_json_pretty(obj, opts = nil)
           ::JSON.pretty_generate(obj, opts_add_max_nesting(opts))
         end
       end
     end
   end

lib/chef/provider/breakpoint.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Provider
       class Breakpoint < Chef::Provider
         
         def load_current_resource
         end
         
         def action_break
           if defined?(Shef) && Shef.running?
             run_context.resource_collection.iterator.pause
             @new_resource.updated_by_last_action(true)
             run_context.resource_collection.iterator
           end
         end
         
       end
     end
   end

lib/chef/resource/gem_package.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/package'
   
   class Chef
     class Resource
       class GemPackage < Chef::Resource::Package
   
         def initialize(name, run_context=nil)
           super
           @resource_name = :gem_package
           @provider = Chef::Provider::Package::Rubygems
         end
   
         def source(arg=nil)
           set_or_return(:source, arg, :kind_of => [ String, Array ])
         end
   
         # Sets a custom gem_binary to run for gem commands.
         def gem_binary(gem_cmd=nil)
           set_or_return(:gem_binary,gem_cmd,:kind_of => [ String ])
         end
   
         ##
         # Options for the gem install, either a Hash or a String. When a hash is
         # given, the options are passed to Gem::DependencyInstaller.new, and the
         # gem will be installed via the gems API. When a String is given, the gem
         # will be installed by shelling out to the gem command. Using a Hash of
         # options with an explicit gem_binary will result in undefined behavior.
         def options(opts=nil)
           set_or_return(:options,opts,:kind_of => [String,Hash])
         end
   
   
       end
     end
   end

lib/chef/knife/node_from_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class NodeFromFile < Knife
   
         deps do
           require 'chef/node'
           require 'chef/json_compat'
           require 'chef/knife/core/object_loader'
         end
   
         banner "knife node from file FILE (options)"
   
         def loader
           @loader ||= Knife::Core::ObjectLoader.new(Chef::Node, ui)
         end
   
         def run
           updated = loader.load_from('nodes', @name_args[0])
   
           updated.save
   
           output(format_for_display(updated)) if config[:print_after]
   
           ui.info("Updated Node #{updated.name}!")
         end
   
       end
     end
   end
   

lib/chef/knife/cookbook_metadata_from_file.rb

   #
   #
   # Author:: Adam Jacob ()
   # Author:: Matthew Kent ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # Copyright:: Copyright (c) 2010 Matthew Kent
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class CookbookMetadataFromFile < Knife
   
         deps do
           require 'chef/cookbook/metadata'
         end
   
         banner "knife cookbook metadata from FILE (options)"
   
         def run
           file = @name_args[0]
           cookbook = File.basename(File.dirname(file))
   
           @metadata = Chef::Knife::CookbookMetadata.new
           @metadata.generate_metadata_from_file(cookbook, file)
         end
   
       end
     end
   end

lib/chef/monkey_patches/string.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   # == String (Patch)
   # On ruby 1.9, Strings are aware of multibyte characters, so +size+ and +length+
   # give the actual number of characters. In Chef::REST, we need the bytesize
   # so we can correctly set the Content-Length headers, but ruby 1.8.6 and lower
   # don't define String#bytesize. Monkey patching time!
   
   begin
     require 'enumerator'
   rescue LoadError
   end
   
   class String
     unless method_defined?(:bytesize)
       alias :bytesize :size
     end
   
     unless method_defined?(:lines)
       def lines
         enum_for(:each)
       end
     end
   end
   
   # <= 1.8.6 needs some ord! 
   class String
     unless method_defined?(:ord)
       def ord
         self.unpack('c').first
       end
     end
   end

lib/chef/knife/role_from_file.rb

   #
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   
   class Chef
     class Knife
       class RoleFromFile < Knife
   
         deps do
           require 'chef/role'
           require 'chef/knife/core/object_loader'
           require 'chef/json_compat'
         end
   
         banner "knife role from file FILE [FILE..] (options)"
   
         def loader
           @loader ||= Knife::Core::ObjectLoader.new(Chef::Role, ui)
         end
   
         option :all,
           :short => "-a",
           :long => "--all",
           :description => "Upload all roles, rather than just a single role"
   
         def run
           @name_args.each do |arg|
             updated = loader.load_from("roles", arg)
   
             updated.save
   
             output(format_for_display(updated)) if config[:print_after]
   
             ui.info("Updated Role #{updated.name}!")
           end
         end
   
       end
     end
   end
   
   
   
   
   

lib/chef/application/knife.rb

   #
   # Author:: Adam Jacob (
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/knife'
   require 'chef/application'
   require 'mixlib/log'
   require 'ohai/config'
   
   class Chef::Application::Knife < Chef::Application
   
     NO_COMMAND_GIVEN = "You need to pass a sub-command (e.g., knife SUB-COMMAND)\n"
   
     banner "Usage: knife sub-command (options)"
   
     option :config_file,
       :short => "-c CONFIG",
       :long  => "--config CONFIG",
       :description => "The configuration file to use",
       :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
   
     verbosity_level = 0
     option :verbosity,
       :short => '-V',
       :long  => '--verbose',
       :description => "More verbose output. Use twice for max verbosity",
       :proc  => Proc.new { verbosity_level += 1},
       :default => 0
   
     option :color,
       :long         => '--color',
       :boolean      => true,
       :default      => true,
       :description  => "Use colored output"
   
     option :no_color,
       :long         => '--no-color',
       :boolean      => true,
       :default      => false,
       :description  => "Don't use colors in the output"
   
     option :environment,
       :short        => "-E ENVIRONMENT",
       :long         => "--environment ENVIRONMENT",
       :description  => "Set the Chef environment"
   
     option :editor,
       :short        => "-e EDITOR",
       :long         => "--editor EDITOR",
       :description  => "Set the editor to use for interactive commands",
       :default      => ENV['EDITOR']
   
     option :no_editor,
       :short        => "-n",
       :long         => "--no-editor",
       :description  => "Do not open EDITOR, just accept the data as is",
       :boolean      => true
   
     option :help,
       :short        => "-h",
       :long         => "--help",
       :description  => "Show this message",
       :on           => :tail,
       :boolean      => true
   
     option :node_name,
       :short => "-u USER",
       :long => "--user USER",
       :description => "API Client Username"
   
     option :client_key,
       :short => "-k KEY",
       :long => "--key KEY",
       :description => "API Client Key",
       :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
   
     option :chef_server_url,
       :short => "-s URL",
       :long => "--server-url URL",
       :description => "Chef Server URL"
   
     option :yes,
       :short => "-y",
       :long => "--yes",
       :description => "Say yes to all prompts for confirmation"
   
     option :defaults,
       :long => "--defaults",
       :description => "Accept default values for all questions"
   
     option :print_after,
       :long => "--print-after",
       :description => "Show the data after a destructive operation"
   
     option :format,
       :short => "-F FORMAT",
       :long => "--format FORMAT",
       :description => "Which format to use for output",
       :default => "summary"
   
     option :version,
       :short        => "-v",
       :long         => "--version",
       :description  => "Show chef version",
       :boolean      => true,
       :proc         => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
       :exit         => 0
   
   
     # Run knife
     def run
       Mixlib::Log::Formatter.show_time = false
       validate_and_parse_options
       quiet_traps
       Chef::Knife.run(ARGV, options)
       exit 0
     end
   
     private
   
     def quiet_traps
       trap("TERM") do
         exit 1
       end
   
       trap("INT") do
         exit 2
       end
     end
   
     def validate_and_parse_options
       # Checking ARGV validity *before* parse_options because parse_options
       # mangles ARGV in some situations
       if no_command_given?
         print_help_and_exit(1, NO_COMMAND_GIVEN)
       elsif no_subcommand_given?
         if (want_help? || want_version?)
           print_help_and_exit
         else
           print_help_and_exit(2, NO_COMMAND_GIVEN)
         end
       end
     end
   
     def no_subcommand_given?
       ARGV[0] =~ /^-/
     end
   
     def no_command_given?
       ARGV.empty?
     end
   
     def want_help?
       ARGV[0] =~ /^(--help|-h)$/
     end
   
     def want_version?
       ARGV[0] =~ /^(--version|-v)$/
     end
   
     def print_help_and_exit(exitcode=1, fatal_message=nil)
       Chef::Log.error(fatal_message) if fatal_message
   
       begin
         self.parse_options
       rescue OptionParser::InvalidOption => e
         puts "#{e}\n"
       end
       puts self.opt_parser
       puts
       Chef::Knife.list_commands
       exit exitcode
     end
   
   end

lib/chef/mixin/get_source_from_package.rb

   # Author:: Lamont Granquist ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   #
   # mixin to make this syntax work without specifying a source:
   #
   # gem_pacakge "/tmp/foo-x.y.z.gem"
   # rpm_package "/tmp/foo-x.y-z.rpm"
   # dpkg_package "/tmp/foo-x.y.z.deb"
   #
   
   class Chef
     module Mixin
       module GetSourceFromPackage
         def initialize(new_resource, run_context)
           super
           # if we're passed something that looks like a filesystem path, with no source, use it
           #  - require at least one '/' in the path to avoid gem_package "foo" breaking if a file named 'foo' exists in the cwd
           if new_resource.source.nil? && new_resource.package_name.match(/#{::File::SEPARATOR}/) && ::File.exists?(new_resource.package_name)
             Chef::Log.debug("No package source specified, but #{new_resource.package_name} exists on the filesystem, copying to package source")
             new_resource.source(@new_resource.package_name)
           end
         end
       end
     end
   end
   

lib/chef/provider/ruby_block.rb

   #
   # Author:: Adam Jacob ()
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2009 Opscode
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   class Chef
     class Provider
       class RubyBlock < Chef::Provider
         def load_current_resource
           true
         end
   
         def action_create
           @new_resource.block.call
   				Chef::Log.info("#{@new_resource} called")
           @new_resource.updated_by_last_action(true)
         end
       end
     end
   end

lib/chef/resource/breakpoint.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   
   require 'chef/resource'
   
   class Chef
     class Resource
       class Breakpoint < Chef::Resource
         
         def initialize(action="break", *args)
           @name = caller.first
           super(@name, *args)
           @action = "break"
           @allowed_actions << :break
           @provider = Chef::Provider::Breakpoint
         end
       end
     end
   end

lib/chef/knife/recipe_list.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/knife'
   class Chef::Knife::RecipeList < Chef::Knife
   
     banner "knife recipe list [PATTERN]"
   
     def run
       recipes = rest.get_rest('cookbooks/_recipes')
       if pattern = @name_args.first
         recipes = recipes.grep(Regexp.new(pattern))
       end
       output(recipes)
     end
   
   end

lib/chef/mash.rb

   # Copyright (c) 2009 Dan Kubb
   
   # Permission is hereby granted, free of charge, to any person obtaining
   # a copy of this software and associated documentation files (the
   # "Software"), to deal in the Software without restriction, including
   # without limitation the rights to use, copy, modify, merge, publish,
   # distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so, subject to
   # the following conditions:
   
   # The above copyright notice and this permission notice shall be
   # included in all copies or substantial portions of the Software.
   
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   
   # ---
   # ---
   
   # Some portions of blank.rb and mash.rb are verbatim copies of software
   # licensed under the MIT license. That license is included below:
   
   # Copyright (c) 2005-2008 David Heinemeier Hansson
   
   # Permission is hereby granted, free of charge, to any person obtaining
   # a copy of this software and associated documentation files (the
   # "Software"), to deal in the Software without restriction, including
   # without limitation the rights to use, copy, modify, merge, publish,
   # distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so, subject to
   # the following conditions:
   
   # The above copyright notice and this permission notice shall be
   # included in all copies or substantial portions of the Software.
   
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   
   # This class has dubious semantics and we only have it so that people can write
   # params[:key] instead of params['key'].
   class Mash < Hash
   
     # @param constructor
     #   The default value for the mash. Defaults to an empty hash.
     #
     # @details [Alternatives]
     #   If constructor is a Hash, a new mash will be created based on the keys of
     #   the hash and no default value will be set.
     def initialize(constructor = {})
       if constructor.is_a?(Hash)
         super()
         update(constructor)
       else
         super(constructor)
       end
     end
   
     # @param key The default value for the mash. Defaults to nil.
     #
     # @details [Alternatives]
     #   If key is a Symbol and it is a key in the mash, then the default value will
     #   be set to the value matching the key.
     def default(key = nil)
       if key.is_a?(Symbol) && include?(key = key.to_s)
         self[key]
       else
         super
       end
     end
   
     alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
     alias_method :regular_update, :update unless method_defined?(:regular_update)
   
     # @param key The key to set.
     # @param value
     #   The value to set the key to.
     #
     # @see Mash#convert_key
     # @see Mash#convert_value
     def []=(key, value)
       regular_writer(convert_key(key), convert_value(value))
     end
   
     # @param other_hash
     #   A hash to update values in the mash with. The keys and the values will be
     #   converted to Mash format.
     #
     # @return [Mash] The updated mash.
     def update(other_hash)
       other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
       self
     end
   
     alias_method :merge!, :update
   
     # @param key The key to check for. This will be run through convert_key.
     #
     # @return [Boolean] True if the key exists in the mash.
     def key?(key)
       super(convert_key(key))
     end
   
     # def include? def has_key? def member?
     alias_method :include?, :key?
     alias_method :has_key?, :key?
     alias_method :member?, :key?
   
     # @param key The key to fetch. This will be run through convert_key.
     # @param *extras Default value.
     #
     # @return [Object] The value at key or the default value.
     def fetch(key, *extras)
       super(convert_key(key), *extras)
     end
   
     # @param *indices
     #   The keys to retrieve values for. These will be run through +convert_key+.
     #
     # @return [Array] The values at each of the provided keys
     def values_at(*indices)
       indices.collect {|key| self[convert_key(key)]}
     end
   
     # @param hash The hash to merge with the mash.
     #
     # @return [Mash] A new mash with the hash values merged in.
     def merge(hash)
       self.dup.update(hash)
     end
   
     # @param key
     #   The key to delete from the mash.\
     def delete(key)
       super(convert_key(key))
     end
   
     # @param *rejected
     #
     # @return [Mash] A new mash without the selected keys.
     #
     # @example
     #   { :one => 1, :two => 2, :three => 3 }.except(:one)
     #     #=> { "two" => 2, "three" => 3 }
     def except(*keys)
       super(*keys.map {|k| convert_key(k)})
     end
   
     # Used to provide the same interface as Hash.
     #
     # @return [Mash] This mash unchanged.
     def stringify_keys!; self end
   
     # @return [Hash] The mash as a Hash with symbolized keys.
     def symbolize_keys
       h = Hash.new(default)
       each { |key, val| h[key.to_sym] = val }
       h
     end
   
     # @return [Hash] The mash as a Hash with string keys.
     def to_hash
       Hash.new(default).merge(self)
     end
   
     # @return [Mash] Convert a Hash into a Mash
     # The input Hash's default value is maintained
     def self.from_hash(hash)
       mash = Mash.new(hash)
       mash.default = hash.default
       mash
     end
   
     protected
     # @param key The key to convert.
     #
     # @param [Object]
     #   The converted key. If the key was a symbol, it will be converted to a
     #   string.
     #
     # @api private
     def convert_key(key)
       key.kind_of?(Symbol) ? key.to_s : key
     end
   
     # @param value The value to convert.
     #
     # @return [Object]
     #   The converted value. A Hash or an Array of hashes, will be converted to
     #   their Mash equivalents.
     #
     # @api private
     def convert_value(value)
       if value.class == Hash
         Mash.from_hash(value)
       elsif value.is_a?(Array)
         value.collect { |e| convert_value(e) }
       else
         value
       end
     end
   end

lib/chef/resource/perl.rb

lib/chef/resource/log.rb

lib/chef/resource/dpkg_package.rb

lib/chef/resource/csh.rb

lib/chef/provider/log.rb

lib/chef/resource/bash.rb

lib/chef/resource/rpm_package.rb

lib/chef/resource/portage_package.rb

lib/chef/resource/macports_package.rb

lib/chef/resource/python.rb

lib/chef/resource/ruby.rb

lib/chef/resource/apt_package.rb

lib/chef/resource/git.rb

lib/chef/resource/pacman_package.rb

lib/chef/resource/freebsd_package.rb

lib/chef/cookbook/file_vendor.rb

lib/chef/knife/node_list.rb

lib/chef/provider/deploy/timestamped.rb

lib/chef/handler/error_report.rb

lib/chef/resource_definition_list.rb

lib/chef/handler.rb

   #--
   # Author:: Adam Jacob ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   require 'chef/client'
   require 'forwardable'
   
   class Chef
     # == Chef::Handler
     # The base class for an Exception or Notification Handler. Create your own
     # handler by subclassing Chef::Handler. When a Chef run fails with an
     # uncaught Exception, Chef will set the +run_status+ on your handler and call
     # +report+
     #
     # ===Example:
     #
     #   require 'net/smtp'
     #
     #   module MyOrg
     #     class OhNoes < Chef::Handler
     #
     #       def report
     #         # Create the email message
     #         message  = "From: Your Name \n"
     #         message << "To: Destination Address \n"
     #         message << "Subject: Chef Run Failure\n"
     #         message << "Date: #{Time.now.rfc2822}\n\n"
     #
     #         # The Node is available as +node+
     #         message << "Chef run failed on #{node.name}\n"
     #         # +run_status+ is a value object with all of the run status data
     #         message << "#{run_status.formatted_exception}\n"
     #         # Join the backtrace lines. Coerce to an array just in case.
     #         message << Array(backtrace).join("\n")
     #
     #         # Send the email
     #         Net::SMTP.start('your.smtp.server', 25) do |smtp|
     #           smtp.send_message message, 'from@address', 'to@address'
     #         end
     #       end
     #
     #     end
     #   end
     #
     class Handler
   
       # The list of currently configured start handlers
       def self.start_handlers
         Array(Chef::Config[:start_handlers])
       end
   
       # Run the start handlers. This will usually be called by a notification
       # from Chef::Client
       def self.run_start_handlers(run_status)
         Chef::Log.info("Running start handlers")
         start_handlers.each do |handler|
           handler.run_report_safely(run_status)
         end
         Chef::Log.info("Start handlers complete.")
       end
   
       # Wire up a notification to run the start handlers when the chef run
       # starts.
       Chef::Client.when_run_starts do |run_status|
         run_start_handlers(run_status)
       end
   
       # The list of currently configured report handlers
       def self.report_handlers
         Array(Chef::Config[:report_handlers])
       end
   
       # Run the report handlers. This will usually be called by a notification
       # from Chef::Client
       def self.run_report_handlers(run_status)
         Chef::Log.info("Running report handlers")
         report_handlers.each do |handler|
           handler.run_report_safely(run_status)
         end
         Chef::Log.info("Report handlers complete")
       end
   
       # Wire up a notification to run the report handlers if the chef run
       # succeeds.
       Chef::Client.when_run_completes_successfully do |run_status|
         run_report_handlers(run_status)
       end
   
       # The list of currently configured exception handlers
       def self.exception_handlers
         Array(Chef::Config[:exception_handlers])
       end
   
       # Run the exception handlers. Usually will be called by a notification
       # from Chef::Client when the run fails.
       def self.run_exception_handlers(run_status)
         Chef::Log.error("Running exception handlers")
         exception_handlers.each do |handler|
           handler.run_report_safely(run_status)
         end
         Chef::Log.error("Exception handlers complete")
       end
   
       # Wire up a notification to run the exception handlers if the chef run fails.
       Chef::Client.when_run_fails do |run_status|
         run_exception_handlers(run_status)
       end
   
       extend Forwardable
   
       # The Chef::RunStatus object containing data about the Chef run.
       attr_reader :run_status
   
       ##
       # :method: start_time
       #
       # The time the chef run started
       def_delegator :@run_status, :start_time
   
       ##
       # :method: end_time
       #
       # The time the chef run ended
       def_delegator :@run_status, :end_time
   
       ##
       # :method: elapsed_time
       #
       # The time elapsed between the start and finish of the chef run
       def_delegator :@run_status, :elapsed_time
   
       ##
       # :method: run_context
       #
       # The Chef::RunContext object used by the chef run
       def_delegator :@run_status, :run_context
   
       ##
       # :method: exception
       #
       # The uncaught Exception that terminated the chef run, or nil if the run
       # completed successfully
       def_delegator :@run_status, :exception
   
       ##
       # :method: backtrace
       #
       # The backtrace captured by the uncaught exception that terminated the chef
       # run, or nil if the run completed successfully
       def_delegator :@run_status, :backtrace
   
       ##
       # :method: node
       #
       # The Chef::Node for this client run
       def_delegator :@run_status, :node
   
       ##
       # :method: all_resources
       #
       # An Array containing all resources in the chef run's resource_collection
       def_delegator :@run_status, :all_resources
   
       ##
       # :method: updated_resources
       #
       # An Array containing all resources that were updated during the chef run
       def_delegator :@run_status, :updated_resources
   
       ##
       # :method: success?
       #
       # Was the chef run successful? True if the chef run did not raise an
       # uncaught exception
       def_delegator :@run_status, :success?
   
       ##
       # :method: failed?
       #
       # Did the chef run fail? True if the chef run raised an uncaught exception
       def_delegator :@run_status, :failed?
   
       # The main entry point for report handling. Subclasses should override this
       # method with their own report handling logic.
       def report
       end
   
       # Runs the report handler, rescuing and logging any errors it may cause.
       # This ensures that all handlers get a chance to run even if one fails.
       # This method should not be overridden by subclasses unless you know what
       # you're doing.
       def run_report_safely(run_status)
         run_report_unsafe(run_status)
       rescue Exception => e
         Chef::Log.error("Report handler #{self.class.name} raised #{e.inspect}")
         Array(e.backtrace).each { |line| Chef::Log.error(line) }
       ensure
         @run_status = nil
       end
   
       # Runs the report handler without any error handling. This method should
       # not be used directly except in testing.
       def run_report_unsafe(run_status)
         @run_status = run_status
         report
       end
   
       # Return the Hash representation of the run_status
       def data
         @run_status.to_hash
       end
   
     end
   end

lib/chef/provider/service/invokercd.rb

lib/chef/exceptions.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Seth Falcon ()
   # Copyright:: Copyright 2008-2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   class Chef
     # == Chef::Exceptions
     # Chef's custom exceptions are all contained within the Chef::Exceptions
     # namespace.
     class Exceptions
       class Application < RuntimeError; end
       class Cron < RuntimeError; end
       class Env < RuntimeError; end
       class Exec < RuntimeError; end
       class ErlCall < RuntimeError; end
       class FileNotFound < RuntimeError; end
       class Package < RuntimeError; end
       class Service < RuntimeError; end
       class Route < RuntimeError; end
       class SearchIndex < RuntimeError; end
       class Override < RuntimeError; end
       class UnsupportedAction < RuntimeError; end
       class MissingLibrary < RuntimeError; end
       class MissingRole < RuntimeError; end
       class CannotDetermineNodeName < RuntimeError; end
       class User < RuntimeError; end
       class Group < RuntimeError; end
       class Link < RuntimeError; end
       class Mount < RuntimeError; end
       class CouchDBNotFound < RuntimeError; end
       class PrivateKeyMissing < RuntimeError; end
       class CannotWritePrivateKey < RuntimeError; end
       class RoleNotFound < RuntimeError; end
       class ValidationFailed < ArgumentError; end
       class InvalidPrivateKey < ArgumentError; end
       class ConfigurationError < ArgumentError; end
       class RedirectLimitExceeded < RuntimeError; end
       class AmbiguousRunlistSpecification < ArgumentError; end
       class CookbookNotFound < RuntimeError; end
       # Cookbook loader used to raise an argument error when cookbook not found.
       # for back compat, need to raise an error that inherits from ArgumentError
       class CookbookNotFoundInRepo < ArgumentError; end
       class AttributeNotFound < RuntimeError; end
       class InvalidCommandOption < RuntimeError; end
       class CommandTimeout < RuntimeError; end
       class ShellCommandFailed < RuntimeError; end
       class RequestedUIDUnavailable < RuntimeError; end
       class InvalidHomeDirectory < ArgumentError; end
       class DsclCommandFailed < RuntimeError; end
       class UserIDNotFound < ArgumentError; end
       class GroupIDNotFound < ArgumentError; end
       class InvalidResourceReference < RuntimeError; end
       class ResourceNotFound < RuntimeError; end
       class InvalidResourceSpecification < ArgumentError; end
       class SolrConnectionError < RuntimeError; end
       class IllegalChecksumRevert < RuntimeError; end
       class CookbookVersionNameMismatch < ArgumentError; end
       class MissingParentDirectory < RuntimeError; end
       class UnresolvableGitReference < RuntimeError; end
       class InvalidEnvironmentRunListSpecification < ArgumentError; end
       class InvalidDataBagItemID < ArgumentError; end
       class InvalidDataBagName < ArgumentError; end
       class EnclosingDirectoryDoesNotExist < ArgumentError; end
   
       class ObsoleteDependencySyntax < ArgumentError; end
       class InvalidDataBagPath < ArgumentError; end
   
       # A different version of a cookbook was added to a
       # VersionedRecipeList than the one already there.
       class CookbookVersionConflict < ArgumentError ; end
   
       # does not follow X.Y.Z format. ArgumentError?
       class InvalidCookbookVersion < ArgumentError; end
   
       # version constraint should be a string or array, or it doesn't
       # match OP VERSION. ArgumentError?
       class InvalidVersionConstraint < ArgumentError; end
   
       class CookbookVersionSelection
   
         # Compound exception: In run_list expansion and resolution,
         # run_list items referred to cookbooks that don't exist and/or
         # have no versions available.
         class InvalidRunListItems < StandardError
           attr_reader :non_existent_cookbooks
           attr_reader :cookbooks_with_no_matching_versions
   
           def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
             super(message)
   
             @non_existent_cookbooks = non_existent_cookbooks
             @cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions
           end
   
           def to_json(*a)
             result = {
               "message" => message,
               "non_existent_cookbooks" => non_existent_cookbooks,
               "cookbooks_with_no_versions" => cookbooks_with_no_matching_versions
             }
             result.to_json(*a)
           end
         end
   
         # In run_list expansion and resolution, a constraint was
         # unsatisfiable.
         #
         # This exception may not be the complete error report. If you
         # resolve the misconfiguration represented by this exception and
         # re-solve, you may get another exception
         class UnsatisfiableRunListItem < StandardError
           attr_reader :run_list_item
           attr_reader :non_existent_cookbooks, :most_constrained_cookbooks
   
           # most_constrained_cookbooks: if I were to remove constraints
           # regarding these cookbooks, I would get a solution or move on
           # to the next error (deeper in the graph). An item in this list
           # may be unsatisfiable, but when resolved may also reveal
           # further unsatisfiable constraints; this condition would not be
           # reported.
           def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks)
             super(message)
   
             @run_list_item = run_list_item
             @non_existent_cookbooks = non_existent_cookbooks
             @most_constrained_cookbooks = most_constrained_cookbooks
           end
   
           def to_json(*a)
             result = {
               "message" => message,
               "unsatisfiable_run_list_item" => run_list_item,
               "non_existent_cookbooks" => non_existent_cookbooks,
               "most_constrained_cookbooks" => most_constrained_cookbooks
             }
             result.to_json(*a)
           end
         end
       end
     end
   end

lib/chef/cookbook/cookbook_collection.rb

   #--
   # Author:: Tim Hinderliter ()
   # Author:: Christopher Walters ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/mash'
   
   class Chef
     # == Chef::CookbookCollection
     # This class is the consistent interface for a node to obtain its
     # cookbooks by name.
     #
     # This class is basically a glorified Hash, but since there are
     # several ways this cookbook information is collected,
     # (e.g. CookbookLoader for solo, hash of auto-vivified Cookbook
     # objects for lazily-loaded remote cookbooks), it gets transformed
     # into this.
     class CookbookCollection < Mash
   
       # The input is a mapping of cookbook name to CookbookVersion objects. We
       # simply extract them
       def initialize(cookbook_versions={})
         super() do |hash, key|
           raise Chef::Exceptions::CookbookNotFound, "Cookbook #{key} not found. " <<
             "If you're loading #{key} from another cookbook, make sure you configure the dependency in your metadata"
         end
         cookbook_versions.each{ |cookbook_name, cookbook_version| self[cookbook_name] = cookbook_version }
       end
   
     end
   end

lib/chef/resource/timestamped_deploy.rb

lib/chef/knife/client_list.rb

lib/chef/knife/role_list.rb

lib/chef/knife/cookbook_list.rb

lib/chef/knife/environment_list.rb

lib/chef/mixin/checksum.rb

lib/chef/knife/data_bag_list.rb

lib/chef/log.rb

lib/chef/knife/cookbook_site_vendor.rb

lib/chef/mixin/xml_escape.rb

   #--
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009 Opscode, Inc.
   # Copyright:: Copyright (c) 2005 Sam Ruby
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   #--
   # Portions of this code are adapted from Sam Ruby's xchar.rb
   # http://intertwingly.net/stories/2005/09/28/xchar.rb 
   #
   # Such code appears here under Sam's original MIT license, while portions of
   # this file are covered by the above Apache License.  For a completely MIT
   # licensed version, please see Sam's original.
   #
   # Thanks, Sam!
   # 
   # Copyright (c) 2005, Sam Ruby 
   # 
   # Permission is hereby granted, free of charge, to any person obtaining a copy
   # of this software and associated documentation files (the "Software"), to deal
   # in the Software without restriction, including without limitation the rights
   # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   # copies of the Software, and to permit persons to whom the Software is
   # furnished to do so, subject to the following conditions:
   # 
   # The above copyright notice and this permission notice shall be included in
   # all copies or substantial portions of the Software.
   # 
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   # THE SOFTWARE.
   
   require 'chef/log'
   
   begin
     require 'fast_xs'
   rescue LoadError
     Chef::Log.info "The fast_xs gem is not installed, slower pure ruby XML escaping will be used."
   end
   
   class Chef
     module Mixin
       module XMLEscape
   
         module PureRuby
           extend self
   
           CP1252 = {
             128 => 8364, # euro sign
             130 => 8218, # single low-9 quotation mark
             131 =>  402, # latin small letter f with hook
             132 => 8222, # double low-9 quotation mark
             133 => 8230, # horizontal ellipsis
             134 => 8224, # dagger
             135 => 8225, # double dagger
             136 =>  710, # modifier letter circumflex accent
             137 => 8240, # per mille sign
             138 =>  352, # latin capital letter s with caron
             139 => 8249, # single left-pointing angle quotation mark
             140 =>  338, # latin capital ligature oe
             142 =>  381, # latin capital letter z with caron
             145 => 8216, # left single quotation mark
             146 => 8217, # right single quotation mark
             147 => 8220, # left double quotation mark
             148 => 8221, # right double quotation mark
             149 => 8226, # bullet
             150 => 8211, # en dash
             151 => 8212, # em dash
             152 =>  732, # small tilde
             153 => 8482, # trade mark sign
             154 =>  353, # latin small letter s with caron
             155 => 8250, # single right-pointing angle quotation mark
             156 =>  339, # latin small ligature oe
             158 =>  382, # latin small letter z with caron
             159 =>  376 # latin capital letter y with diaeresis
           }
   
           # http://www.w3.org/TR/REC-xml/#dt-chardata
           PREDEFINED = {
             38 => '&', # ampersand
             60 => '<',  # left angle bracket
             62 => '>'  # right angle bracket
           }
   
           # http://www.w3.org/TR/REC-xml/#charsets
           VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF), 
             (0xE000..0xFFFD), (0x10000..0x10FFFF)]
   
           def xml_escape(unescaped_str)
             begin
               unescaped_str.unpack("U*").map {|char| xml_escape_char!(char)}.join
             rescue
               unescaped_str.unpack("C*").map {|char| xml_escape_char!(char)}.join
             end
           end
   
           private
   
           def xml_escape_char!(char)
             char = CP1252[char] || char
             char = 42 unless VALID.detect {|range| range.include? char}
             char = PREDEFINED[char] || (char<128 ? char.chr : "&##{char};")
           end
         end
         
         module FastXS
           extend self
   
           def xml_escape(string)
             string.fast_xs
           end
   
         end
   
         if "strings".respond_to?(:fast_xs)
           include FastXS
           extend FastXS
         else
           include PureRuby
           extend PureRuby
         end
       end
     end
   end

lib/chef/config.rb

   #
   # Author:: Adam Jacob ()
   # Author:: Christopher Brown ()
   # Author:: AJ Christensen ()
   # Copyright:: Copyright (c) 2008 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
   require 'chef/log'
   require 'mixlib/config'
   
   class Chef
     class Config
   
       extend Mixlib::Config
   
       # Manages the chef secret session key
       # === Returns
       # :: A new or retrieved session key
       #
       def self.manage_secret_key
         newkey = nil
         if Chef::FileCache.has_key?("chef_server_cookie_id")
           newkey = Chef::FileCache.load("chef_server_cookie_id")
         else
           chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
           newkey = ""
           40.times { |i| newkey << chars[rand(chars.size-1)] }
           Chef::FileCache.store("chef_server_cookie_id", newkey)
         end
         newkey
       end
   
       def self.inspect
         configuration.inspect
       end
   
       # Override the config dispatch to set the value of multiple server options simultaneously
       #
       # === Parameters
       # url:: String to be set for all of the chef-server-api URL's
       #
       config_attr_writer :chef_server_url do |url|
         configure do |c|
           [ :registration_url,
             :template_url,
             :remotefile_url,
             :search_url,
             :chef_server_url,
             :role_url ].each do |u|
               c[u] = url
           end
         end
         url
       end
   
       # When you are using ActiveSupport, they monkey-patch 'daemonize' into Kernel.
       # So while this is basically identical to what method_missing would do, we pull
       # it up here and get a real method written so that things get dispatched
       # properly.
       config_attr_writer :daemonize do |v|
         configure do |c|
           c[:daemonize] = v
         end
       end
   
       # Override the config dispatch to set the value of log_location configuration option
       #
       # === Parameters
       # location:: Logging location as either an IO stream or string representing log file path
       #
       config_attr_writer :log_location do |location|
         if location.respond_to? :sync=
           location.sync = true
           location
         elsif location.respond_to? :to_str
           f = File.new(location.to_str, "a")
           f.sync = true
           f
         end
       end
   
       # Override the config dispatch to set the value of authorized_openid_providers when openid_providers (deprecated) is used
       #
       # === Parameters
       # providers:: An array of openid providers that are authorized to login to the chef server
       #
       config_attr_writer :openid_providers do |providers|
         configure { |c| c[:authorized_openid_providers] = providers }
         providers
       end
   
       # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
       enforce_path_sanity(true)
   
       # Used when OpenID authentication is enabled in the Web UI
       authorized_openid_identifiers nil
       authorized_openid_providers nil
       openid_cstore_couchdb false
       openid_cstore_path "/var/chef/openid/cstore"
   
       # The number of times the client should retry when registering with the server
       client_registration_retries 5
   
       # Where the cookbooks are located. Meaning is somewhat context dependent between
       # knife, chef-client, and chef-solo.
       cookbook_path [ "/var/chef/cookbooks", "/var/chef/site-cookbooks" ]
   
       # Where files are stored temporarily during uploads
       sandbox_path "/var/chef/sandboxes"
   
       # Where cookbook files are stored on the server (by content checksum)
       checksum_path "/var/chef/checksums"
   
       # CouchDB database name to use
       couchdb_database "chef"
   
       couchdb_url "http://localhost:5984"
   
       # Where chef's cache files should be stored
       file_cache_path "/var/chef/cache"
   
       # Where backups of chef-managed files should go
       file_backup_path "/var/chef/backup"
   
       ## Daemonization Settings ##
       # What user should Chef run as?
       user nil
       # What group should the chef-server, -solr, -solr-indexer run as
       group nil
       umask 0022
   
       http_retry_count 5
       http_retry_delay 5
       interval nil
       json_attribs nil
       log_level :info
       log_location STDOUT
       # toggle info level log items that can create a lot of output
       verbose_logging true 
       node_name nil
       node_path "/var/chef/node"
   
       pid_file nil
   
       chef_server_url   "http://localhost:4000"
       registration_url  "http://localhost:4000"
       template_url      "http://localhost:4000"
       role_url          "http://localhost:4000"
       remotefile_url    "http://localhost:4000"
       search_url        "http://localhost:4000"
   
       client_url "http://localhost:4042"
   
       rest_timeout 300
       run_command_stderr_timeout 120
       run_command_stdout_timeout 120
       solo  false
       splay nil
   
       # Set these to enable SSL authentication / mutual-authentication
       # with the server
       ssl_client_cert nil
       ssl_client_key nil
       ssl_verify_mode :verify_none
       ssl_ca_path nil
       ssl_ca_file nil
   
   
       # Where should chef-solo look for role files?
       role_path "/var/chef/roles"
   
       # Where should chef-solo download recipes from?
       recipe_url nil
   
       solr_url "http://localhost:8983"
       solr_jetty_path "/var/chef/solr-jetty"
       solr_data_path "/var/chef/solr/data"
       solr_home_path "/var/chef/solr"
       solr_heap_size "256M"
       solr_java_opts nil
   
       # Parameters for connecting to RabbitMQ
       amqp_host '0.0.0.0'
       amqp_port '5672'
       amqp_user 'chef'
       amqp_pass 'testing'
       amqp_vhost '/chef'
       # Setting this to a UUID string also makes the queue durable
       # (persist across rabbitmq restarts)
       amqp_consumer_id "default"
   
       client_key "/etc/chef/client.pem"
       validation_key "/etc/chef/validation.pem"
       validation_client_name "chef-validator"
       web_ui_client_name "chef-webui"
       web_ui_key "/etc/chef/webui.pem"
       web_ui_admin_user_name  "admin"
       web_ui_admin_default_password "p@ssw0rd1"
   
       # Server Signing CA
       #
       # In truth, these don't even have to change
       signing_ca_cert "/var/chef/ca/cert.pem"
       signing_ca_key "/var/chef/ca/key.pem"
       signing_ca_user nil
       signing_ca_group nil
       signing_ca_country "US"
       signing_ca_state "Washington"
       signing_ca_location "Seattle"
       signing_ca_org "Chef User"
       signing_ca_domain "opensource.opscode.com"
       signing_ca_email "opensource-cert@opscode.com"
   
       # Report Handlers
       report_handlers []
   
       # Exception Handlers
       exception_handlers []
   
       # Start handlers
       start_handlers []
   
       # Checksum Cache
       # Uses Moneta on the back-end
       cache_type "BasicFile"
       cache_options({ :path => "/var/chef/cache/checksums", :skip_expires => true })
   
       # Arbitrary knife configuration data
       knife Hash.new
   
       # Those lists of regular expressions define what chef considers a
       # valid user and group name
       user_valid_regex [ /^([-a-zA-Z0-9_.]+)$/, /^\d+$/ ]
       group_valid_regex [ /^([-a-zA-Z0-9_.\\ ]+)$/, /^\d+$/ ]
     end
   end

lib/chef/knife/core/subcommand_loader.rb

   # Author:: Christopher Brown ()
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2009, 2011 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   # 
   #     http://www.apache.org/licenses/LICENSE-2.0
   # 
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/version'
   class Chef
     class Knife
       class SubcommandLoader
   
         CHEF_FILE_IN_GEM = /chef-[\d]+\.[\d]+\.[\d]+/
         CURRENT_CHEF_GEM = /chef-#{Regexp.escape(Chef::VERSION)}/
   
         attr_reader :chef_config_dir
         attr_reader :env
   
         def initialize(chef_config_dir, env=ENV)
           @chef_config_dir, @env = chef_config_dir, env
           @forced_activate = {}
         end
   
         # Load all the sub-commands
         def load_commands
           subcommand_files.each { |subcommand| Kernel.load subcommand }
           true
         end
   
         # Returns an Array of paths to knife commands located in chef_config_dir/plugins/knife/
         # and ~/.chef/plugins/knife/
         def site_subcommands
           user_specific_files = []
   
           if chef_config_dir
             user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", chef_config_dir))
           end
   
           # finally search ~/.chef/plugins/knife/*.rb
           user_specific_files.concat Dir.glob(File.join(env['HOME'], '.chef', 'plugins', 'knife', '*.rb'))
   
           user_specific_files
         end
   
         # Returns a Hash of paths to knife commands built-in to chef, or installed via gem.
         # If rubygems is not installed, falls back to globbing the knife directory.
         # The Hash is of the form {"relative/path" => "/absolute/path"}
         #--
         # Note: the "right" way to load the plugins is to require the relative path, i.e.,
         #   require 'chef/knife/command'
         # but we're getting frustrated by bugs at every turn, and it's slow besides. So
         # subcommand loader has been modified to load the plugins by using Kernel.load
         # with the absolute path.
         def gem_and_builtin_subcommands
           # search all gems for chef/knife/*.rb
           require 'rubygems'
           find_subcommands_via_rubygems
         rescue LoadError
           find_subcommands_via_dirglob
         end
   
         def subcommand_files
           @subcommand_files ||= (gem_and_builtin_subcommands.values + site_subcommands).flatten.uniq
         end
   
         def find_subcommands_via_dirglob
           # The "require paths" of the core knife subcommands bundled with chef
           files = Dir[File.expand_path('../../../knife/*.rb', __FILE__)]
           subcommand_files = {}
           files.each do |knife_file|
             rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1]
             subcommand_files[rel_path] = knife_file
           end
           subcommand_files
         end
   
         def find_subcommands_via_rubygems
           files = Gem.find_files 'chef/knife/*.rb'
           files.reject! {|f| from_old_gem?(f) }
           subcommand_files = {}
           files.each do |file|
             rel_path = file[/(#{Regexp.escape File.join('chef', 'knife', '')}.*)\.rb/, 1]
             subcommand_files[rel_path] = file
           end
   
           subcommand_files.merge(find_subcommands_via_dirglob)
         end
   
         private
   
         # wow, this is a sad hack :(
         # Gem.find_files finds files in all versions of a gem, which
         # means that if chef 0.10 and 0.9.x are installed, we'll try to
         # require, e.g., chef/knife/ec2_server_create, which will cause
         # a gem activation error. So remove files from older chef gems.
         def from_old_gem?(path)
           path =~ CHEF_FILE_IN_GEM && path !~ CURRENT_CHEF_GEM
         end
       end
     end
   end

lib/chef/mixins.rb

lib/chef/shef/shef_rest.rb

lib/chef/providers.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/provider/breakpoint'
   require 'chef/provider/cookbook_file'
   require 'chef/provider/cron'
   require 'chef/provider/cron/solaris'
   require 'chef/provider/deploy'
   require 'chef/provider/directory'
   require 'chef/provider/env'
   require 'chef/provider/erl_call'
   require 'chef/provider/execute'
   require 'chef/provider/file'
   require 'chef/provider/git'
   require 'chef/provider/group'
   require 'chef/provider/http_request'
   require 'chef/provider/ifconfig'
   require 'chef/provider/link'
   require 'chef/provider/log'
   require 'chef/provider/ohai'
   require 'chef/provider/mdadm'
   require 'chef/provider/mount'
   require 'chef/provider/package'
   require 'chef/provider/remote_directory'
   require 'chef/provider/remote_file'
   require 'chef/provider/route'
   require 'chef/provider/ruby_block'
   require 'chef/provider/script'
   require 'chef/provider/service'
   require 'chef/provider/subversion'
   require 'chef/provider/template'
   require 'chef/provider/user'
   
   require 'chef/provider/env/windows'
   
   require 'chef/provider/package/apt'
   require 'chef/provider/package/dpkg'
   require 'chef/provider/package/easy_install'
   require 'chef/provider/package/freebsd'
   require 'chef/provider/package/macports'
   require 'chef/provider/package/pacman'
   require 'chef/provider/package/portage'
   require 'chef/provider/package/rpm'
   require 'chef/provider/package/rubygems'
   require 'chef/provider/package/yum'
   require 'chef/provider/package/zypper'
   require 'chef/provider/package/solaris'
   
   require 'chef/provider/service/arch'
   require 'chef/provider/service/debian'
   require 'chef/provider/service/freebsd'
   require 'chef/provider/service/gentoo'
   require 'chef/provider/service/init'
   require 'chef/provider/service/insserv'
   require 'chef/provider/service/redhat'
   require 'chef/provider/service/simple'
   require 'chef/provider/service/systemd'
   require 'chef/provider/service/upstart'
   require 'chef/provider/service/windows'
   require 'chef/provider/service/solaris'
   
   require 'chef/provider/user/dscl'
   require 'chef/provider/user/pw'
   require 'chef/provider/user/useradd'
   require 'chef/provider/user/windows'
   
   require 'chef/provider/group/aix'
   require 'chef/provider/group/dscl'
   require 'chef/provider/group/gpasswd'
   require 'chef/provider/group/groupadd'
   require 'chef/provider/group/pw'
   require 'chef/provider/group/suse'
   require 'chef/provider/group/usermod'
   require 'chef/provider/group/windows'
   
   require 'chef/provider/mount/mount'
   require 'chef/provider/mount/windows'
   
   require 'chef/provider/deploy/revision'
   require 'chef/provider/deploy/timestamped'

lib/chef.rb

lib/chef/resources.rb

   #
   # Author:: Daniel DeLeo ()
   # Copyright:: Copyright (c) 2010 Opscode, Inc.
   # License:: Apache License, Version 2.0
   #
   # Licensed under the Apache License, Version 2.0 (the "License");
   # you may not use this file except in compliance with the License.
   # You may obtain a copy of the License at
   #
   #     http://www.apache.org/licenses/LICENSE-2.0
   #
   # Unless required by applicable law or agreed to in writing, software
   # distributed under the License is distributed on an "AS IS" BASIS,
   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   # See the License for the specific language governing permissions and
   # limitations under the License.
   #
   
   require 'chef/resource/apt_package'
   require 'chef/resource/bash'
   require 'chef/resource/breakpoint'
   require 'chef/resource/cookbook_file'
   require 'chef/resource/cron'
   require 'chef/resource/csh'
   require 'chef/resource/deploy'
   require 'chef/resource/deploy_revision'
   require 'chef/resource/directory'
   require 'chef/resource/dpkg_package'
   require 'chef/resource/easy_install_package'
   require 'chef/resource/env'
   require 'chef/resource/erl_call'
   require 'chef/resource/execute'
   require 'chef/resource/file'
   require 'chef/resource/freebsd_package'
   require 'chef/resource/gem_package'
   require 'chef/resource/git'
   require 'chef/resource/group'
   require 'chef/resource/http_request'
   require 'chef/resource/ifconfig'
   require 'chef/resource/link'
   require 'chef/resource/log'
   require 'chef/resource/macports_package'
   require 'chef/resource/mdadm'
   require 'chef/resource/mount'
   require 'chef/resource/ohai'
   require 'chef/resource/package'
   require 'chef/resource/pacman_package'
   require 'chef/resource/perl'
   require 'chef/resource/portage_package'
   require 'chef/resource/python'
   require 'chef/resource/remote_directory'
   require 'chef/resource/remote_file'
   require 'chef/resource/rpm_package'
   require 'chef/resource/route'
   require 'chef/resource/ruby'
   require 'chef/resource/ruby_block'
   require 'chef/resource/scm'
   require 'chef/resource/script'
   require 'chef/resource/service'
   require 'chef/resource/subversion'
   require 'chef/resource/template'
   require 'chef/resource/timestamped_deploy'
   require 'chef/resource/user'
   require 'chef/resource/yum_package'

lib/chef/nil_argument.rb

lib/chef/applications.rb

lib/chef/knife/help_topics.rb

lib/chef/index_queue.rb

lib/chef/application/agent.rb

Generated on Thu Oct 20 17:05:40 +0530 2011